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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// The sidebar section settings in cmux.json (`sidebar.sectionLook`,
/// `sidebar.topBandMaxShare`, `sidebar.bottomBandMaxShare`,
/// `sidebar.pinnedBandsScroll` and `sidebar.showWorkspaceTabs`;
/// `sidebar.pinnedBandsScroll`, `sidebar.showWorkspaceTabs` and
/// `sidebar.showCounts`;
/// plans/cmux-next/sidebar-sections.md 7).
/// `sidebar.minimalMode`: which pinned bands hide until the pointer is over
/// the sidebar (R54).
Expand Down Expand Up @@ -28,6 +29,8 @@ public nonisolated struct SidebarSectionsPreferences: Hashable, Sendable {
public var pinnedBandsScroll: Bool
/// Whether the workspace list expands each workspace into its tab rows.
public var showWorkspaceTabs: Bool
/// Each workspace row shows how many tabs it has.
public var showCounts = false
/// Pinned bands that hide until the pointer is over the sidebar (R54).
/// R100: the Settings/account band shows only while the pointer is over the sidebar.
public var minimalMode: SidebarMinimalMode = .bottom
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

131 changes: 131 additions & 0 deletions Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -60265,6 +60265,137 @@
}
}
},
"settings.sidebar.showCounts": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "إظهار عدد علامات التبويب"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Prikaži broj kartica"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Vis antal faner"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Anzahl der Tabs anzeigen"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Show Tab Counts"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Mostrar número de pestañas"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Afficher le nombre d’onglets"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Mostra numero di schede"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "タブ数を表示"
}
},
"km": {
"stringUnit": {
"state": "translated",
"value": "បង្ហាញចំនួនផ្ទាំង"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "탭 개수 표시"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Vis antall faner"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Pokaż liczbę kart"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Mostrar número de abas"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Показывать число вкладок"
}
},
"th": {
"stringUnit": {
"state": "translated",
"value": "แสดงจำนวนแท็บ"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "Sekme sayısını göster"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Показувати кількість вкладок"
}
},
"vi": {
"stringUnit": {
"state": "translated",
"value": "Hiện số tab"
}
},
"zh-Hans": {
"stringUnit": {
"state": "translated",
"value": "显示标签页数量"
}
},
"zh-Hant": {
"stringUnit": {
"state": "translated",
"value": "顯示分頁數量"
}
}
}
},
"settings.source.ghostty": {
"extractionState": "manual",
"localizations": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ extension SettingsSchema {
"sidebar.sectionLook",
"sidebar.topBandMaxShare",
"sidebar.bottomBandMaxShare",
"sidebar.pinnedBandsScroll", "sidebar.showWorkspaceTabs",
"sidebar.pinnedBandsScroll", "sidebar.showWorkspaceTabs", "sidebar.showCounts",
"browser.defaultEngine",
"browser.newTabPage",
"browser.showBookmarksBar",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ nonisolated enum SidebarSectionSettingsSchema {
keywords: ["sidebar", "sections", "pinned", "scroll"]
),
SidebarSectionsSetting.showWorkspaceTabsDescriptor(group: sidebar),
SidebarSectionsSetting.showCountsDescriptor(group: sidebar),
SidebarSectionsSetting.minimalModeDescriptor(group: sidebar),
ChromePlacementSetting.sidebarSideDescriptor(group: sidebar),
ChromePlacementSetting.spacesPositionDescriptor(group: sidebar),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public nonisolated enum SidebarSectionsSetting {
static let legacyScrollPath = ["sidebar", "stickyBandsScroll"]
public static let showWorkspaceTabsPath = ["sidebar", "showWorkspaceTabs"]
public static let minimalModePath = ["sidebar", "minimalMode"]
public static let showCountsPath = ["sidebar", "showCounts"]

static func minimalModeDescriptor(group: SettingText) -> SettingDescriptor {
SettingDescriptor(minimalModePath, section: .appearance, group: group,
Expand All @@ -36,6 +37,14 @@ public nonisolated enum SidebarSectionsSetting {
kind: .toggle, default: .bool(SidebarSectionsPreferences.defaults.showWorkspaceTabs),
keywords: ["sidebar", "workspace", "tabs"])
}

static func showCountsDescriptor(group: SettingText) -> SettingDescriptor {
SettingDescriptor(showCountsPath, section: .appearance, group: group,
title: SettingsText.keyed("settings.sidebar.showCounts", "Show Tab Counts"),
kind: .toggle, default: .bool(SidebarSectionsPreferences.defaults.showCounts),
keywords: ["sidebar", "workspace", "tabs", "count", "number"])
}

/// The looks the setting accepts (CmuxNextSidebar.SectionsLookVariant).
public static let looks = ["quiet", "card", "tray", "lines", "linesIcons"]

Expand Down Expand Up @@ -84,9 +93,19 @@ public nonisolated enum SidebarSectionsSetting {
}
}
SidebarNavigationSetting.parse(root, into: &result.navigation, diagnostics: &diagnostics)
result.showCounts = flag(root, showCountsPath, fallback: result.showCounts, &diagnostics)
return result
}

private static func flag(_ root: JSONValue, _ path: [String], fallback: Bool, _ diagnostics: inout [SettingsDiagnostic]) -> Bool {
guard let value = root.value(at: path) else { return fallback }
guard let flag = value.boolValue else {
diagnostics.append(SettingsDiagnostic(kind: .invalidValue, path: path.joined(separator: "."), message: "expected true or false"))
return fallback
}
return flag
}

private static func share(_ root: JSONValue, _ path: [String], _ name: String, fallback: Double,
_ diagnostics: inout [SettingsDiagnostic]) -> Double {
guard let value = root.value(at: path) else { return fallback }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ public nonisolated struct SidebarLayout: Hashable, Sendable {
!o.excludedWorkspaces.contains(ws.id) && (o.filterMatches?.contains(ws.id) ?? true)
}

// `showWorkspaceTabs` lists every workspace's tabs, except those
// the user collapsed with the row's disclosure.
func listsTabs(_ ws: SidebarWorkspace) -> Bool {
o.showWorkspaceTabs && !o.collapsedWorkspaces.contains(ws.id)
}

func disclosure(_ ws: SidebarWorkspace) -> SidebarTabDisclosure? {
guard o.showWorkspaceTabs else { return nil }
if ws.tabs.isEmpty { return .empty }
return o.collapsedWorkspaces.contains(ws.id) ? .collapsed : .expanded
}

func openGapIfNeeded(section: SectionID, group: GroupID?, index: Int) {
guard gapY == nil, let gap = o.gap, gap.section == section, gap.group == group, gap.index == index else { return }
gapY = y
Expand Down Expand Up @@ -132,10 +144,11 @@ public nonisolated struct SidebarLayout: Hashable, Sendable {
rows.append(SidebarRow(
key: .workspace(ws.id), y: y, height: h, section: section.id,
group: nil, siblingIndex: index, parentIndex: nil, isLastInGroup: false,
isCollapsed: false, childCount: 0, groupColor: nil
isCollapsed: false, childCount: 0, groupColor: nil,
tabDisclosure: disclosure(ws), tabCount: o.showCounts ? ws.tabs.count : nil
))
y += h + m.rowSpacing
if o.showWorkspaceTabs {
if listsTabs(ws) {
for tab in ws.tabs {
rows.append(SidebarRow(
key: .tab(ws.id, tab.id), y: y, height: m.tabRowHeight, section: section.id,
Expand All @@ -162,10 +175,11 @@ public nonisolated struct SidebarLayout: Hashable, Sendable {
key: .workspace(ws.id), y: y, height: h, section: section.id,
group: group.id, siblingIndex: childIndex, parentIndex: index,
isLastInGroup: childIndex == entry.children.count - 1,
isCollapsed: false, childCount: 0, groupColor: group.color
isCollapsed: false, childCount: 0, groupColor: group.color,
tabDisclosure: disclosure(ws), tabCount: o.showCounts ? ws.tabs.count : nil
))
y += h + m.rowSpacing
if o.showWorkspaceTabs {
if listsTabs(ws) {
for tab in ws.tabs {
rows.append(SidebarRow(
key: .tab(ws.id, tab.id), y: y, height: m.tabRowHeight, section: section.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public nonisolated struct SidebarLayoutOptions: Hashable, Sendable {
public var showsSoleMachineHeader = false
/// Include tab rows beneath each visible workspace.
public var showWorkspaceTabs = false
/// With `showWorkspaceTabs`, the workspaces whose disclosure hid their tabs.
public var collapsedWorkspaces: Set<WorkspaceID> = []
/// Workspace rows show their tab count (`sidebar.showCounts`).
public var showCounts = false

public init() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ public nonisolated enum SidebarRowKey: Hashable, Sendable {
case emptySection(SectionID)
}

/// A workspace row's control for its inline tabs (`sidebar.showWorkspaceTabs`).
public nonisolated enum SidebarTabDisclosure: Hashable, Sendable {
/// The workspace has no tabs: the control's place stays, drawn empty.
case empty
case collapsed
case expanded
}

/// One laid-out row in document (flipped) coordinates.
public nonisolated struct SidebarRow: Hashable, Sendable {
public var key: SidebarRowKey
Expand All @@ -38,6 +46,10 @@ public nonisolated struct SidebarRow: Hashable, Sendable {
public var tabKind: SidebarTabKind? = nil
/// A machine header standing for the only machine: titled "Projects".
public var titlesProjects = false
/// A workspace row's tab disclosure; nil when `sidebar.showWorkspaceTabs` is off.
public var tabDisclosure: SidebarTabDisclosure? = nil
/// A workspace row's tab count, when `sidebar.showCounts` is on.
public var tabCount: Int? = nil

public var maxY: CGFloat { y + height }
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public final class SidebarModel {
@ObservationIgnored public var onCardAction: ((String, SidebarCardAction) -> Void)?
/// Whether each workspace expands to show its intra-workspace tabs.
public var showWorkspaceTabs = false
/// The workspaces whose disclosure hid their tabs: window view state.
public var collapsedWorkspaces: Set<WorkspaceID> = []
/// Workspace rows show their tab count (`sidebar.showCounts`).
public var showCounts = false
/// Machine sections list loose workspaces before groups (a daemon-backed
/// sidebar: cmux-tui keeps no slot for one after a group), so a drag
/// never offers a slot past the first group.
Expand Down Expand Up @@ -243,6 +247,27 @@ public final class SidebarModel {
return true
}

/// The `sidebar.*` settings that shape the workspace list.
func applyListPreferences(_ preferences: SidebarSectionsPreferences) {
showWorkspaceTabs = preferences.showWorkspaceTabs
showCounts = preferences.showCounts
}

/// The disclosure on a workspace row: hide its listed tabs, or list them again.
public func toggleWorkspaceTabs(_ id: WorkspaceID) {
if collapsedWorkspaces.remove(id) == nil { collapsedWorkspaces.insert(id) }
}

/// The list layout's options that come from the model.
func listOptions() -> SidebarLayoutOptions {
var o = SidebarLayoutOptions()
o.filterMatches = filterMatches
o.showWorkspaceTabs = showWorkspaceTabs
o.collapsedWorkspaces = collapsedWorkspaces
o.showCounts = showCounts
return o
}

// MARK: Presentation

/// Next (+1) or previous (-1) profile, clamped at the ends. Returns
Expand Down
Loading
Loading