Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions Sources/Cloud/CloudTeamPickerHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ struct CloudTeamPickerHeader<AgentMenu: View, Status: View>: View {
}
}
Spacer(minLength: 0)
agentMenu()
// Invite is the only action surfaced in the Cloud sidebar header.
// Machine creation, refresh, and the agent menu remain available
// through their command and menu entry points without competing
Comment thread
coderabbitai[bot] marked this conversation as resolved.
// with the team invite affordance here.
if let accountFlow, accountFlow.confirmedTeamID != nil {
MachinesChromeLabelButton(
symbolName: "person.badge.plus",
Expand All @@ -81,25 +84,6 @@ struct CloudTeamPickerHeader<AgentMenu: View, Status: View>: View {
}
.accessibilityIdentifier("CloudTeamInviteButton")
}
switch actions {
case .inline:
MachinesChromeIconButton(
symbolName: "arrow.clockwise",
accessibilityLabel: refreshLabel,
isBusy: isRefreshing,
action: onRefresh
)
.accessibilityIdentifier("CloudHeaderRefreshButton")
MachinesChromeIconButton(
symbolName: "plus",
accessibilityLabel: newMachineLabel,
isBusy: false,
action: onNewMachine
)
.accessibilityIdentifier("CloudHeaderNewMachineButton")
case .overflowMenu:
machineActionsMenu
}
}
}

Expand Down
11 changes: 10 additions & 1 deletion Sources/Cloud/MachinesPanelView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,16 @@ struct MachinesPanelView: View {
} else if isTeamChangePending {
teamScopeLoading
} else {
content
VStack(spacing: 0) {
HStack {
Spacer(minLength: 0)
cloudAgentMenu
}
.padding(.horizontal, RightSidebarChromeMetrics.barHorizontalPadding)
.padding(.vertical, 2)
.accessibilityIdentifier("CloudMachinesAgentActionsRow")
content
}
}
}

Expand Down
16 changes: 8 additions & 8 deletions cmuxTests/CloudMachinesHeaderCountTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ struct CloudMachinesHeaderCountTests {
#expect(CloudTreeRowContentView.groupCount(for: .cloudMachinesSection(canCreateMachine: true)) == nil)
}

// `ViewThatFits` shows the inline row only when its ideal width fits the
// bar, and falls back to the overflow menu otherwise. Measuring that ideal
// width needs no accessibility client, unlike reading SwiftUI's tree.
// The header still measures both team-name layout candidates. Measuring
// their ideal widths needs no accessibility client, unlike reading the
// SwiftUI tree.
@Test("Narrow Cloud headers move machine actions into one overflow menu")
func narrowHeaderCollapsesMachineActions() async throws {
let inline = try await idealRowWidth(.inline, teamName: Self.longTeamName)
#expect(inline > Self.barContentWidth(220),
"The inline row (\(inline)pt) fits a 220pt sidebar, so the overflow menu never shows")
}

@Test("A wide Cloud header keeps refresh and new machine buttons inline")
func wideHeaderKeepsMachineActionsInline() async throws {
@Test("A wide Cloud header keeps its action row stable")
func wideHeaderKeepsActionRowStable() async throws {
let inline = try await idealRowWidth(.inline, teamName: "Team A")
#expect(inline <= Self.barContentWidth(420),
"The inline row (\(inline)pt) overflows a 420pt sidebar, so Refresh and New Machine fold away")
// The overflow row trades two buttons for one menu, so it never needs more room.
"The header action row (\(inline)pt) overflows a 420pt sidebar")
let overflow = try await idealRowWidth(.overflowMenu, teamName: "Team A")
#expect(overflow < inline)
#expect(overflow == inline,
"The Invite-only header should not change width between layout candidates")
}

@Test("A free plan at its limit turns orange and names the upgrade", arguments: [
Expand Down
Loading