Skip to content
6 changes: 3 additions & 3 deletions .github/swift-file-length-budget.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,24 @@
1009 cmuxTests/CmuxTopSnapshotScopeTests.swift
1006 cmuxTests/CmuxSSHURLRequestTests.swift
1002 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/PairedMacBackupTests.swift
982 Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
953 cmuxTests/DockSocketLifecycleTests.swift
951 Sources/App/TerminalDirectoryOpenSupport.swift
982 Packages/Shared/CmuxAgentChat/Tests/CmuxAgentChatTests/ChatConversationStoreTests.swift
948 Sources/App/ShortcutRoutingSupport.swift
947 Sources/TerminalNotificationPolicy.swift
945 Sources/SessionIndexRegisteredAgents.swift
937 Sources/Panels/BrowserPopupWindowController.swift
937 Sources/TextBoxMentionIndexStore.swift
928 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AppSection.swift
920 Sources/CommandPalette/CommandPaletteSettingsToggle.swift
755 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
905 Sources/CmuxSSHURLRequest.swift
899 Sources/Panels/MarkdownWebRenderer.swift
885 Sources/Panels/TerminalPanel.swift
885 cmuxTests/SidebarWorkspaceDropPlannerTests.swift
881 Sources/DockSplitStore.swift
871 cmuxTests/ClaudeHookSurfaceResolutionSwiftTests.swift
868 Sources/Panels/BrowserScreenshotSnapshotter.swift
864 Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
859 Packages/macOS/CmuxControlSocket/Sources/CmuxControlSocket/Coordinator/Workspace/ControlCommandCoordinator+Workspace.swift
847 cmuxTests/AgentSessionAutoResumeSettingsTests.swift
845 cmuxTests/SSHStartupSignalLifecycleTests.swift
Expand All @@ -124,13 +124,13 @@
773 Sources/App/MenuBarExtraController.swift
769 Packages/macOS/CmuxTerminal/Sources/CmuxTerminal/Surface/TerminalSurface+Input.swift
768 cmuxUITests/BrowserFixtureInteractionUITests.swift
864 Packages/Shared/CmuxAgentChat/Sources/CmuxAgentChat/Store/ChatConversationStore.swift
762 Packages/iOS/CmuxMobileTransport/Sources/CmuxMobileTransport/CmxNetworkByteTransport.swift
761 Sources/Mobile/AgentChat/AgentChatSessionRegistry.swift
760 Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/AgentLaunchSanitizerTests.swift
756 Sources/Panels/AgentSessionWebRendererCoordinator.swift
754 Sources/TerminalController+ControlWorkspaceContext.swift
752 cmuxUITests/CloseWorkspaceCmdDUITests.swift
743 Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/WorkspaceDetailView.swift
739 cmuxTests/CLICodexHookTimeoutRegressionTests.swift
738 Packages/macOS/CMUXProjectModel/Sources/CMUXProjectModel/XcodeProjectAdapter.swift
718 Packages/Shared/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator.swift
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if DEBUG
import CmuxAgentChat
import CmuxAgentChatUI
import CmuxMobileShellModel
import CmuxMobileSupport
import Foundation
import SwiftUI
Expand Down Expand Up @@ -72,26 +73,13 @@ struct AgentChatDemoScreen: View {
ToolbarSpacer(.fixed, placement: .topBarLeading)
}
ToolbarItem(placement: .topBarLeading) {
Menu {
Button(L10n.string("mobile.workspace.rename.title", defaultValue: "Rename Workspace")) {}
.accessibilityIdentifier("MobileWorkspaceTitleRenameMenuItem")
Button(L10n.string("mobile.workspace.markRead", defaultValue: "Mark as Read")) {}
.accessibilityIdentifier("MobileWorkspaceTitleMarkReadMenuItem")
} label: {
WorkspaceToolbarTitleControl(
contentWidth: contentWidth,
hasBackButton: true,
hasChatToggle: true
) {
header(for: stack)
.frame(
minWidth: MobileNavTitleWidth.floor,
maxWidth: MobileNavTitleWidth(
contentWidth: contentWidth,
hasBackButton: true,
hasChatToggle: true
).leadingCap,
alignment: .leading
)
.layoutPriority(1)
}
.mobileGlassCompactToolbarControl()
.accessibilityIdentifier("MobileWorkspaceTitleMenu")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
Expand All @@ -103,9 +91,32 @@ struct AgentChatDemoScreen: View {
Image(systemName: "bubble.left.and.bubble.right.fill")
}
.accessibilityIdentifier("AgentChatInlinePreviewChatToggle")
Button(action: {}) {
Image(systemName: "rectangle.stack")
Menu {
WorkspaceTitleMenuContent(
workspace: inlinePreviewWorkspace,
canCloseWorkspace: true,
presentRename: {},
toggleReadState: {},
requestClose: {}
)

Section {
Button {} label: {
Label(
L10n.string("mobile.terminal.new", defaultValue: "New Terminal"),
systemImage: "plus"
)
}
.accessibilityIdentifier("MobileNewTerminalMenuItem")
}
} label: {
Label(
L10n.string("mobile.terminal.picker.menuTitle", defaultValue: "Workspace and Terminals"),
systemImage: "rectangle.stack"
)
.labelStyle(.iconOnly)
}
.accessibilityLabel(L10n.string("mobile.terminal.picker.menuTitle", defaultValue: "Workspace and Terminals"))
.accessibilityIdentifier("AgentChatInlinePreviewTerminalPicker")
}
}
Expand Down Expand Up @@ -139,6 +150,28 @@ struct AgentChatDemoScreen: View {
) ?? "cmux"
}

private var inlinePreviewWorkspace: MobileWorkspacePreview {
var workspace = MobileWorkspacePreview(
id: "inline-preview-workspace",
name: inlineWorkspaceTitle ?? "cmux",
hasUnread: true,
terminals: [
MobileTerminalPreview(
id: "inline-preview-terminal",
name: inlineWorkspaceSubtitle
?? L10n.string("mobile.terminal.select", defaultValue: "Terminal"),
isFocused: true
),
]
)
workspace.actionCapabilities = MobileWorkspaceActionCapabilities(
supportsWorkspaceActions: true,
supportsReadStateActions: true,
supportsCloseActions: true
)
return workspace
}

private var inlineWorkspaceSubtitle: String? {
guard style == .inlineWorkspace else { return nil }
return UITestConfig.value(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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
Expand All @@ -28,8 +28,6 @@ struct WorkspaceChatPane<TitleMenuContent: View>: View {
/// Compact-stack back button owned by the workspace toolbar, colocated with
/// the leading title so their order is deterministic.
let backButtonConfiguration: WorkspaceBackButtonConfiguration?
/// 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

Expand Down Expand Up @@ -66,9 +64,11 @@ struct WorkspaceChatPane<TitleMenuContent: View>: View {
}
}
ToolbarItem(placement: .topBarLeading) {
Menu {
titleMenuContent()
} label: {
WorkspaceToolbarTitleControl(
contentWidth: contentWidth,
hasBackButton: backButtonConfiguration != nil,
hasChatToggle: true
) {
ChatSessionHeaderView(
descriptor: conversation.descriptor,
agentState: conversation.agentState,
Expand All @@ -77,19 +77,7 @@ struct WorkspaceChatPane<TitleMenuContent: View>: View {
subtitle: tabName,
style: .toolbarCompact
)
.frame(
minWidth: MobileNavTitleWidth.floor,
maxWidth: MobileNavTitleWidth(
contentWidth: contentWidth,
hasBackButton: backButtonConfiguration != nil,
hasChatToggle: true
).leadingCap,
alignment: .leading
)
.layoutPriority(1)
}
.mobileGlassCompactToolbarControl()
.accessibilityIdentifier("MobileWorkspaceTitleMenu")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ extension WorkspaceDetailView {
set: { chatDrafts[session.id] = $0 }
),
backButtonConfiguration: backButtonConfiguration,
titleMenuContent: { titleMenuContent },
onExitChat: {
withAnimation(.snappy(duration: 0.28)) {
isChatMode = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,10 @@ struct WorkspaceDetailView: View {
}
}
ToolbarItem(placement: .topBarLeading) {
WorkspaceTitleMenu(
WorkspaceToolbarTitleControl(
contentWidth: contentWidth,
hasBackButton: backButtonConfiguration != nil,
hasChatToggle: shouldShowChatToggle,
menuContent: { titleMenuContent }
hasChatToggle: shouldShowChatToggle
) {
Text(browser.title ?? workspace.name)
.font(.headline)
Expand Down Expand Up @@ -319,11 +318,10 @@ struct WorkspaceDetailView: View {
}
}
ToolbarItem(placement: .topBarLeading) {
WorkspaceTitleMenu(
WorkspaceToolbarTitleControl(
contentWidth: contentWidth,
hasBackButton: backButtonConfiguration != nil,
hasChatToggle: shouldShowChatToggle,
menuContent: { titleMenuContent }
hasChatToggle: shouldShowChatToggle
) {
WorkspaceToolbarTitleView(title: workspace.name, subtitle: selectedToolbarSubtitle)
}
Expand Down Expand Up @@ -412,13 +410,13 @@ struct WorkspaceDetailView: View {
terminalPickerMenuContent
} label: {
Label(
selectedTerminal?.name ?? L10n.string("mobile.terminal.select", defaultValue: "Terminal"),
L10n.string("mobile.terminal.picker.menuTitle", defaultValue: "Workspace and Terminals"),
systemImage: "rectangle.stack"
)
.labelStyle(.iconOnly)
}
.foregroundStyle(TerminalPalette.foreground)
.accessibilityLabel(L10n.string("mobile.terminal.picker.title", defaultValue: "Terminals"))
.accessibilityLabel(L10n.string("mobile.terminal.picker.menuTitle", defaultValue: "Workspace and Terminals"))
.accessibilityIdentifier("MobileTerminalDropdown")
.accessibilityValue(host)
}
Expand All @@ -441,6 +439,8 @@ struct WorkspaceDetailView: View {
}
}

titleMenuContent

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Section {
Button(action: createWorkspaceFromToolbar) {
Label(L10n.string("mobile.workspace.new", defaultValue: "New Workspace"), systemImage: "plus.square.on.square")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import CmuxMobileSupport
import SwiftUI

/// Rename-workspace dialog (an alert with an inline text field) shared by the
/// workspace detail view's title menu across the terminal / chat /
/// browser panes. Reuses the same strings as the workspace list's rename sheet so
/// both entrypoints read identically.
/// workspace detail view's picker menu across the terminal / chat / browser panes.
/// Reuses the same strings as the workspace list's rename sheet so both
/// entrypoints read identically.
extension View {
func workspaceRenameDialog(
isPresented: Binding<Bool>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import CmuxMobileSupport
import SwiftUI

struct WorkspaceTitleMenu<Label: View, MenuContent: View>: View {
struct WorkspaceToolbarTitleControl<Label: View>: View {
let contentWidth: CGFloat
let hasBackButton: Bool
let hasChatToggle: Bool
@ViewBuilder let menuContent: () -> MenuContent
@ViewBuilder let label: () -> Label

var body: some View {
Menu {
menuContent()
} label: {
label()
.frame(
minWidth: MobileNavTitleWidth.floor,
maxWidth: MobileNavTitleWidth(
contentWidth: contentWidth,
hasBackButton: hasBackButton,
hasChatToggle: hasChatToggle
).leadingCap,
alignment: .leading
)
.layoutPriority(1)
}
.mobileGlassCompactToolbarControl()
.accessibilityIdentifier("MobileWorkspaceTitleMenu")
fittedLabel
.mobileGlassCompactNavigationTitle()
.accessibilityIdentifier("MobileWorkspaceTitleMenu")
}
Comment thread
austinywang marked this conversation as resolved.

private var fittedLabel: some View {
label()
.frame(
minWidth: MobileNavTitleWidth.floor,
maxWidth: MobileNavTitleWidth(
contentWidth: contentWidth,
hasBackButton: hasBackButton,
hasChatToggle: hasChatToggle
).leadingCap,
alignment: .leading
)
.layoutPriority(1)
}
}
17 changes: 17 additions & 0 deletions ios/cmux/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -5917,6 +5917,23 @@
}
}
},
"mobile.terminal.picker.menuTitle": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Workspace and Terminals"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "ワークスペースとターミナル"
}
}
}
},
"mobile.terminal.select": {
"extractionState": "manual",
"localizations": {
Expand Down
Loading
Loading