Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import CoreGraphics

/// Width math for the leading glass workspace title menu.
///
/// The workspace title belongs beside the back button, not in the centered
/// principal slot. Reserve the trailing toolbar cluster and the leading back
/// control so the title truncates before it can underlap native toolbar items.
struct MobileLeadingToolbarTitleWidth {
let contentWidth: CGFloat
let hasBackButton: Bool
let hasTrailingCluster: Bool
let hasChatToggle: Bool

static let backButtonReserve: CGFloat = 44
static let trailingReserveBase: CGFloat = 60
static let chatToggleReserve: CGFloat = 56
static let barMarginsAndSpacing: CGFloat = 44
static let unmeasuredFallback: CGFloat = 180
static let floor: CGFloat = 96

var cap: CGFloat {
guard contentWidth > 0 else { return Self.unmeasuredFallback }
let leading = hasBackButton ? Self.backButtonReserve : 0
let trailing = hasTrailingCluster
? Self.trailingReserveBase + (hasChatToggle ? Self.chatToggleReserve : 0)
: 0
return max(0, contentWidth - leading - trailing - Self.barMarginsAndSpacing)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ struct WorkspaceBackButton: View {
Button(action: action) {
HStack(spacing: 5) {
Image(systemName: "chevron.backward")
.fontWeight(.semibold)
.font(.system(size: 17, weight: .semibold))
.imageScale(.medium)
.foregroundStyle(.primary)
.frame(width: 17, height: 22)
if unreadCount > 0 {
Text(countText)
// Smaller than the chevron, on a small mono circle.
Expand All @@ -27,7 +29,6 @@ struct WorkspaceBackButton: View {
.background(badgeFillColor, in: .circle)
}
}
.contentShape(.rect)
}
.accessibilityLabel(accessibilityLabel)
.accessibilityIdentifier("MobileWorkspaceBackButton")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,20 @@ import UIKit
/// The agent chat rendered inline in the workspace detail, in place of the
/// terminal, when chat mode is toggled on. There is no cover and no Done
/// button: the same toolbar toggle flips back to the terminal.
struct WorkspaceChatPane<TitleMenuContent: View>: View {
struct WorkspaceChatPane: View {
let session: ChatSessionDescriptor
let conversation: ChatConversationStore
let store: CMUXMobileShellStore
/// The owning workspace's name, shown as the header title (so the header
/// reads as the workspace, not the session's first prompt).
let workspaceName: String
/// The name of the tab/terminal this session lives on, shown as the
/// header subtitle.
let tabName: String?
/// Composer draft, owned by the parent so it survives toggling back to
/// the terminal and returning mid-thought.
@Binding var draft: String
/// Compact-stack back button owned by the workspace toolbar.
let backButtonConfiguration: WorkspaceBackButtonConfiguration?
/// Whether the workspace title pill should open a menu.
let isTitleMenuEnabled: Bool
/// Workspace-scoped actions exposed from the title pill.
let titleMenuContent: () -> TitleMenuContent
/// Flips chat mode off (the toggle's "back to terminal" path).
let onExitChat: () -> Void

@Environment(BrowserSurfaceStore.self) private var browserStore

@State private var accessoryConfiguration = TerminalAccessoryConfiguration.shared
@State private var isShowingShortcutSettings = false
/// Full content width, used to bound the centered toolbar header so a long
/// workspace name truncates before the trailing toolbar buttons.
@State private var contentWidth: CGFloat = 0

var body: some View {
Group {
Expand All @@ -53,56 +38,12 @@ struct WorkspaceChatPane<TitleMenuContent: View>: View {
runsStoreTask: false,
onOpenTerminal: openTerminal
)
// The host (workspace detail) owns the nav bar, so the live
// session-state header is supplied here rather than by ChatScreen,
// which would be dropped under the workspace's own chrome.
.onGeometryChange(for: CGFloat.self) { $0.size.width } action: { contentWidth = $0 }
.toolbar {
if backButtonConfiguration != nil {
ToolbarItem(placement: .topBarLeading) {
workspaceBackToolbarButton
}
}
ToolbarItem(placement: .principal) {
// WorkspaceDetailView mounts the terminal picker/chat
// toggle in a sibling trailing toolbar item.
WorkspaceTitleMenu(
contentWidth: contentWidth,
hasBackButton: backButtonConfiguration != nil,
hasTrailingCluster: true,
hasChatToggle: true,
isEnabled: isTitleMenuEnabled,
menuContent: titleMenuContent
) {
ChatSessionHeaderView(
descriptor: conversation.descriptor,
agentState: conversation.agentState,
isConnected: conversation.isConnected,
titleOverride: workspaceName,
subtitle: tabName,
style: .toolbarCompact
)
}
}
}
}
.sheet(isPresented: $isShowingShortcutSettings) {
TerminalShortcutsSettingsView(scope: .agentChat)
}
}

@ViewBuilder
private var workspaceBackToolbarButton: some View {
if let backButtonConfiguration {
WorkspaceBackButton(
unreadCount: backButtonConfiguration.unreadCount,
badgeContrast: backButtonConfiguration.badgeContrast,
action: backButtonConfiguration.action
)
.mobileGlassCompactToolbarControl()
}
}

/// The escape hatch: select the session's terminal surface, then leave
/// chat mode so the terminal shows.
private func openTerminal() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,10 @@ extension WorkspaceDetailView {
session: session,
conversation: conversation,
store: store,
workspaceName: workspace.name,
tabName: tabName(for: session),
draft: Binding(
get: { chatDrafts[session.id] ?? "" },
set: { chatDrafts[session.id] = $0 }
),
backButtonConfiguration: backButtonConfiguration,
isTitleMenuEnabled: hasTitleMenuActions,
titleMenuContent: { titleMenuContent },
onExitChat: {
withAnimation(.snappy(duration: 0.28)) {
isChatMode = false
Expand All @@ -111,22 +106,6 @@ extension WorkspaceDetailView {
.id(session.id)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.mobileChatTopScrollEdgeLayout(legacyTopPadding: terminalTopPadding)
.mobileTerminalNavigationChrome()
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
toolbarTrailingCluster
}
}
.task(id: chatRefreshKey) { await refreshChatSessions() }
.closeWorkspaceConfirmation(
isPresented: $isConfirmingClose,
confirm: confirmCloseWorkspaceFromMenu
)
.workspaceRenameDialog(
isPresented: $isRenamePresented,
text: $renameText,
onSave: commitRenameFromDialog
)
} else {
Color.clear
.task(id: session.id) {
Expand Down
Loading
Loading