Skip to content
Closed
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
8 changes: 5 additions & 3 deletions .github/swift-file-length-budget.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
4483 Sources/cmuxApp.swift
4482 Sources/Panels/FilePreviewPanel.swift
4367 cmuxTests/BrowserPanelTests.swift
4283 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
4121 Sources/BrowserWindowPortal.swift
3964 Packages/iOS/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
3934 Sources/Feed/FeedPanelView.swift
3926 cmuxTests/TabManagerUnitTests.swift
3896 cmuxTests/WindowAndDragTests.swift
Expand All @@ -45,8 +45,8 @@
2229 Sources/TerminalWindowPortal.swift
2126 cmuxTests/CmuxConfigTests.swift
2085 cmuxTests/ShortcutAndCommandPaletteTests.swift
2078 Sources/SessionPersistence.swift
2011 Sources/KeyboardShortcutSettingsFileStore.swift
2081 Sources/SessionPersistence.swift
2019 Sources/KeyboardShortcutSettingsFileStore.swift
1944 Sources/RestorableAgentSession.swift
1900 cmuxTests/NotificationAndMenuBarTests.swift
1866 Sources/Panels/BrowserWebAuthnSupport.swift
Expand Down Expand Up @@ -173,6 +173,7 @@
637 Packages/iOS/CmuxMobileShell/Tests/CmuxMobileShellTests/MobileShellRenderGridLivenessTestSupport.swift
635 cmuxUITests/RightSidebarChromeHeightUITests.swift
630 Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutWhenClause.swift
629 cmuxTests/SidebarWidthPolicyTests.swift
620 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/KeyboardShortcutsSection.swift
620 cmuxTests/FinderFileDropRegressionTests.swift
620 cmuxTests/TerminalNotificationQueueTests.swift
Expand Down Expand Up @@ -211,6 +212,7 @@
560 cmuxTests/CLISSHPTYResizeInputTests.swift
558 Packages/macOS/CmuxGit/Sources/CmuxGit/Parsing/GitMetadataService+Config.swift
553 Sources/RightSidebarPanelView.swift
552 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/SidebarSection.swift
549 Sources/Panels/BrowserAutomation.swift
547 Packages/iOS/CmuxMobileShell/Sources/CmuxMobileShell/BackingUpPairedMacStore.swift
546 Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Sections/AutomationSection.swift
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ struct TerminalViewportInputs {
let toolbarPresentationFrame: CGRect?
}
#endif

Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,3 @@ struct TerminalViewportSnapshot {
}
}
#endif

Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ public struct SidebarCatalogSection: SettingCatalogSection {
userDefaultsKey: "sidebarShowStatusPills"
)

/// Minimum width, in points, the left (workspace) sidebar can be resized
/// to. Normalization and bounds live in ``LeftSidebarWidthSettings``.
public let leftMinWidth = DefaultsKey<Double>(
id: LeftSidebarWidthSettings.settingsPath,
defaultValue: LeftSidebarWidthSettings.defaultMinimumWidth,
userDefaultsKey: LeftSidebarWidthSettings.minimumWidthKey
)

public let rightMaxWidth = DefaultsKey<Double>(
id: "sidebar.rightMaxWidth",
defaultValue: RightSidebarWidthSettings.noOverrideValue,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Foundation

/// Policy values and normalization helpers for the left (workspace) sidebar's
/// minimum resize width.
///
/// The left sidebar can never be dragged narrower than this floor. The floor is
/// configurable so users can reclaim horizontal space for the terminal without
/// hiding the sidebar entirely. Mirrors ``RightSidebarWidthSettings`` so both
/// sidebar width policies live in one place.
public struct LeftSidebarWidthSettings: Sendable {
/// Creates a stateless left sidebar width policy value.
public init() {}

/// The `cmux.json` key under `sidebar` that stores the left sidebar minimum width.
public static let jsonKey = "leftMinWidth"

/// The dotted settings path for the left sidebar minimum width.
public static let settingsPath = "sidebar.leftMinWidth"

/// The `UserDefaults` key that stores the active left sidebar minimum width.
///
/// Kept as the historical `sidebarMinimumWidth` key so existing stored values
/// (and the Titlebar Layout debug window) continue to resolve unchanged.
public static let minimumWidthKey = "sidebarMinimumWidth"

/// The default minimum width, in points, used when no override is active.
///
/// Matches the historical hard-coded floor so a fresh sidebar is unchanged.
public static let defaultMinimumWidth = 216.0

/// The smallest configurable minimum width, in points.
public static let lowerBound = 100.0

/// The largest configurable minimum width, in points.
public static let upperBound = 260.0

/// The supported configurable range for the minimum width.
public static let range: ClosedRange<Double> = lowerBound...upperBound

/// Clamps a requested minimum width to the supported range.
///
/// - Parameter value: The requested minimum width in points.
/// - Returns: A finite minimum width within ``range``, or the default when the
/// value is not finite.
public func clampedMinimumWidth(_ value: Double) -> Double {
guard value.isFinite else {
return Self.defaultMinimumWidth
}
return min(Self.upperBound, max(Self.lowerBound, value))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Testing
@testable import CmuxSettings

@Suite("LeftSidebarWidthSettings")
struct LeftSidebarWidthSettingsTests {
private let settings = LeftSidebarWidthSettings()

@Test func defaultMatchesHistoricalFloor() {
#expect(LeftSidebarWidthSettings.defaultMinimumWidth == 216)
#expect(LeftSidebarWidthSettings.range.contains(LeftSidebarWidthSettings.defaultMinimumWidth))
}

@Test func rangeAllowsNarrowerThanHistoricalMinimum() {
// The point of issue #6784: the floor must be configurable below 216.
#expect(LeftSidebarWidthSettings.lowerBound < 216)
#expect(LeftSidebarWidthSettings.lowerBound == 100)
#expect(LeftSidebarWidthSettings.upperBound == 260)
}

@Test func clampHonorsConfiguredValueWithinRange() {
#expect(settings.clampedMinimumWidth(120) == 120)
#expect(settings.clampedMinimumWidth(216) == 216)
}

@Test func clampPinsOutOfRangeValuesToBounds() {
#expect(settings.clampedMinimumWidth(40) == LeftSidebarWidthSettings.lowerBound)
#expect(settings.clampedMinimumWidth(10_000) == LeftSidebarWidthSettings.upperBound)
}

@Test func clampFallsBackToDefaultForNonFiniteValues() {
#expect(settings.clampedMinimumWidth(.nan) == LeftSidebarWidthSettings.defaultMinimumWidth)
#expect(settings.clampedMinimumWidth(.infinity) == LeftSidebarWidthSettings.defaultMinimumWidth)
}

@Test func identifiersAreStable() {
#expect(LeftSidebarWidthSettings.jsonKey == "leftMinWidth")
#expect(LeftSidebarWidthSettings.settingsPath == "sidebar.leftMinWidth")
#expect(LeftSidebarWidthSettings.minimumWidthKey == "sidebarMinimumWidth")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ extension Array where Element == CuratedSettingEntry {
.init(section: .sidebarAppearance, id: "show-log", title: "Show Latest Log in Sidebar", synonyms: "sidebar.showLog log status latest message imperative"),
.init(section: .sidebarAppearance, id: "show-progress", title: "Show Progress in Sidebar", synonyms: "sidebar.showProgress progress bar percent status set_progress"),
.init(section: .sidebarAppearance, id: "show-metadata", title: "Show Custom Metadata in Sidebar", synonyms: "sidebar.showCustomMetadata metadata meta report_meta status custom block"),
.init(section: .sidebarAppearance, id: "left-min-width", title: String(localized: "settings.sidebar.leftMinWidth", defaultValue: "Sidebar Minimum Width"), synonyms: "sidebar.leftMinWidth left sidebar minimum min width narrow narrower thin resize drag floor reclaim terminal space"),
.init(section: .sidebarAppearance, id: "right-max-width", title: "Dock Max Width", synonyms: "sidebar.rightMaxWidth dock right sidebar max width terminal reservation cap logs lazygit"),

// Mobile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct SidebarSection: View {
private let catalog: SettingCatalog
private let hostActions: SettingsHostActions
private let rightSidebarWidthSettings = RightSidebarWidthSettings()
private let leftSidebarWidthSettings = LeftSidebarWidthSettings()

@State private var sidebarFont: SettingsFontSize
@State private var fontSaveFailed = false
Expand All @@ -35,6 +36,7 @@ public struct SidebarSection: View {
@State private var showLog: DefaultsValueModel<Bool>
@State private var showProgress: DefaultsValueModel<Bool>
@State private var showMetadata: DefaultsValueModel<Bool>
@State private var leftMinWidth: DefaultsValueModel<Double>
@State private var rightMaxWidth: DefaultsValueModel<Double>
@State private var rememberedRightMaxWidth: DefaultsValueModel<Double>

Expand All @@ -61,6 +63,7 @@ public struct SidebarSection: View {
_showLog = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.sidebar.showLog))
_showProgress = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.sidebar.showProgress))
_showMetadata = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.sidebar.showCustomMetadata))
_leftMinWidth = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.sidebar.leftMinWidth))
_rightMaxWidth = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.sidebar.rightMaxWidth))
_rememberedRightMaxWidth = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.sidebar.rememberedRightMaxWidth))
}
Expand Down Expand Up @@ -94,6 +97,7 @@ public struct SidebarSection: View {
showLog,
showProgress,
showMetadata,
leftMinWidth,
rightMaxWidth,
rememberedRightMaxWidth,
]
Expand Down Expand Up @@ -167,6 +171,24 @@ public struct SidebarSection: View {
rightSidebarWidthSettings.clampedSettingsEditorMaximumWidth(value)
}

private var leftMinWidthBinding: Binding<Double> {
Binding(
get: { leftSidebarWidthSettings.clampedMinimumWidth(leftMinWidth.current) },
set: { leftMinWidth.set(leftSidebarWidthSettings.clampedMinimumWidth($0)) }
)
}

private var leftMinWidthValueText: String {
String(Int(leftSidebarWidthSettings.clampedMinimumWidth(leftMinWidth.current).rounded()))
}

private var leftMinWidthIsDefault: Bool {
abs(
leftSidebarWidthSettings.clampedMinimumWidth(leftMinWidth.current)
- LeftSidebarWidthSettings.defaultMinimumWidth
) < 0.5
}

@ViewBuilder
private var mainCard: some View {
SettingsCard {
Expand Down Expand Up @@ -225,6 +247,40 @@ public struct SidebarSection: View {
}
SettingsCardDivider()

SettingsCardRow(
configurationReview: .json("sidebar.leftMinWidth"),
String(localized: "settings.sidebar.leftMinWidth", defaultValue: "Sidebar Minimum Width"),
subtitle: String(localized: "settings.sidebar.leftMinWidth.subtitle", defaultValue: "Smallest width the workspace sidebar can be resized to. Lower it to reclaim space for the terminal without hiding the sidebar."),
controlWidth: 250
) {
HStack(spacing: 8) {
Slider(
value: leftMinWidthBinding,
in: LeftSidebarWidthSettings.range,
step: 1
)
.frame(width: 130)
.accessibilityIdentifier("SettingsSidebarMinimumWidthSlider")
.accessibilityLabel(String(localized: "settings.sidebar.leftMinWidth", defaultValue: "Sidebar Minimum Width"))

Text(String.localizedStringWithFormat(
String(localized: "settings.sidebar.leftMinWidth.value", defaultValue: "%@ pt"),
leftMinWidthValueText
))
.cmuxFont(size: 12, weight: .medium, design: .rounded)
.monospacedDigit()
.frame(width: 44, alignment: .trailing)

Button(String(localized: "settings.sidebar.leftMinWidth.reset", defaultValue: "Reset")) {
leftMinWidth.set(LeftSidebarWidthSettings.defaultMinimumWidth)
}
.buttonStyle(.bordered)
.controlSize(.small)
.disabled(leftMinWidthIsDefault)
}
}
SettingsCardDivider()

SettingsCardRow(
configurationReview: .json("sidebar.rightMaxWidth"),
String(localized: "settings.sidebar.rightMaxWidth", defaultValue: "Dock Max Width"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct SettingsRowAnchorResolutionTests {
"notifications.unreadPaneRing",
"sidebar.branchLayout",
"sidebar.hideAllDetails",
"sidebar.leftMinWidth",
"sidebar.makePullRequestsClickable",
"sidebar.openPortLinksInCmuxBrowser",
"sidebar.openPullRequestLinksInCmuxBrowser",
Expand Down
Loading
Loading