Skip to content

Commit 4e94f27

Browse files
authored
Merge pull request #19 from sidequery/nicosuave/sidebar-grouping
Restore sidebar grouping when sidebar tabs is enabled
2 parents b695683 + 3bfb7cb commit 4e94f27

2 files changed

Lines changed: 76 additions & 42 deletions

File tree

macos/Sources/Features/Terminal/TerminalController.swift

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,33 @@ final class WorktrunkSidebarState: ObservableObject {
2323
self.selection = selection
2424
}
2525

26-
func applyExpandedRepoIDs(_ next: Set<UUID>, listMode: WorktrunkSidebarListMode) {
26+
func applyExpandedRepoIDs(
27+
_ next: Set<UUID>,
28+
listMode: WorktrunkSidebarListMode,
29+
alwaysVisibleWorktreePaths: Set<String> = []
30+
) {
2731
guard next != expandedRepoIDs else { return }
2832
expandedRepoIDs = next
2933
pruneSelectionForVisibility(
3034
listMode: listMode,
3135
expandedRepoIDs: next,
32-
expandedWorktreePaths: expandedWorktreePaths
36+
expandedWorktreePaths: expandedWorktreePaths,
37+
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths
3338
)
3439
}
3540

36-
func applyExpandedWorktreePaths(_ next: Set<String>, listMode: WorktrunkSidebarListMode) {
41+
func applyExpandedWorktreePaths(
42+
_ next: Set<String>,
43+
listMode: WorktrunkSidebarListMode,
44+
alwaysVisibleWorktreePaths: Set<String> = []
45+
) {
3746
guard next != expandedWorktreePaths else { return }
3847
expandedWorktreePaths = next
3948
pruneSelectionForVisibility(
4049
listMode: listMode,
4150
expandedRepoIDs: expandedRepoIDs,
42-
expandedWorktreePaths: next
51+
expandedWorktreePaths: next,
52+
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths
4353
)
4454
}
4555

@@ -115,18 +125,23 @@ final class WorktrunkSidebarState: ObservableObject {
115125
private func pruneSelectionForVisibility(
116126
listMode: WorktrunkSidebarListMode,
117127
expandedRepoIDs: Set<UUID>,
118-
expandedWorktreePaths: Set<String>
128+
expandedWorktreePaths: Set<String>,
129+
alwaysVisibleWorktreePaths: Set<String>
119130
) {
120131
guard let selection else { return }
121132
switch selection {
122133
case .repo:
123134
return
124-
case .worktree(let repoID, _):
125-
if listMode == .nestedByRepo, !expandedRepoIDs.contains(repoID) {
135+
case .worktree(let repoID, let path):
136+
if listMode == .nestedByRepo,
137+
!expandedRepoIDs.contains(repoID),
138+
!alwaysVisibleWorktreePaths.contains(path) {
126139
self.selection = .repo(id: repoID)
127140
}
128141
case .session(_, let repoID, let worktreePath):
129-
if listMode == .nestedByRepo, !expandedRepoIDs.contains(repoID) {
142+
if listMode == .nestedByRepo,
143+
!expandedRepoIDs.contains(repoID),
144+
!alwaysVisibleWorktreePaths.contains(worktreePath) {
130145
self.selection = .repo(id: repoID)
131146
return
132147
}
@@ -1599,6 +1614,12 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
15991614
(NSApp.delegate as? AppDelegate)?.worktrunkStore.sidebarListMode ?? .flatWorktrees
16001615
}
16011616

1617+
private func currentWorktrunkAlwaysVisibleWorktreePaths() -> Set<String> {
1618+
guard WorktrunkPreferences.sidebarTabsEnabled else { return [] }
1619+
guard currentWorktrunkSidebarListMode() == .nestedByRepo else { return [] }
1620+
return Set(openTabsModel.tabs.compactMap(\.worktreeRootPath).map(Self.standardizedPath))
1621+
}
1622+
16021623
private func applySyncedWorktrunkSidebarVisibility(_ visibility: NavigationSplitViewVisibility) {
16031624
guard worktrunkSidebarState.columnVisibility != visibility else { return }
16041625
worktrunkSidebarState.isApplyingRemoteUpdate = true
@@ -1618,7 +1639,11 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
16181639
worktrunkSidebarSyncApplyingRemoteUpdate = false
16191640
worktrunkSidebarState.isApplyingRemoteUpdate = false
16201641
}
1621-
worktrunkSidebarState.applyExpandedRepoIDs(expandedRepoIDs, listMode: currentWorktrunkSidebarListMode())
1642+
worktrunkSidebarState.applyExpandedRepoIDs(
1643+
expandedRepoIDs,
1644+
listMode: currentWorktrunkSidebarListMode(),
1645+
alwaysVisibleWorktreePaths: currentWorktrunkAlwaysVisibleWorktreePaths()
1646+
)
16221647
}
16231648

16241649
private func applySyncedWorktrunkSidebarExpandedWorktreePaths(_ expandedWorktreePaths: Set<String>) {
@@ -1631,7 +1656,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
16311656
}
16321657
worktrunkSidebarState.applyExpandedWorktreePaths(
16331658
expandedWorktreePaths,
1634-
listMode: currentWorktrunkSidebarListMode()
1659+
listMode: currentWorktrunkSidebarListMode(),
1660+
alwaysVisibleWorktreePaths: currentWorktrunkAlwaysVisibleWorktreePaths()
16351661
)
16361662
}
16371663

macos/Sources/Features/Worktrunk/WorktrunkSidebarView.swift

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,14 @@ struct WorktrunkSidebarView: View {
5959

6060
Spacer(minLength: 0)
6161

62-
if !sidebarTabsEnabled {
63-
Button {
64-
toggleSidebarListMode()
65-
} label: {
66-
Image(systemName: store.sidebarListMode == .flatWorktrees ? "list.bullet.indent" : "list.bullet")
67-
.foregroundStyle(.secondary)
68-
}
69-
.buttonStyle(.plain)
70-
.help(store.sidebarListMode == .flatWorktrees ? "Switch to nested list" : "Switch to flat list")
62+
Button {
63+
toggleSidebarListMode()
64+
} label: {
65+
Image(systemName: store.sidebarListMode == .flatWorktrees ? "list.bullet.indent" : "list.bullet")
66+
.foregroundStyle(.secondary)
7167
}
68+
.buttonStyle(.plain)
69+
.help(store.sidebarListMode == .flatWorktrees ? "Switch to nested list" : "Switch to flat list")
7270

7371
Menu {
7472
ForEach(WorktreeSortOrder.allCases, id: \.self) { order in
@@ -187,19 +185,12 @@ struct WorktrunkSidebarView: View {
187185
.onChange(of: store.sidebarListMode) { _ in
188186
clearSelectionIfMainInFlatMode()
189187
}
190-
.onChange(of: sidebarTabsEnabled) { enabled in
191-
if enabled {
192-
forceFlatModeIfNeeded()
193-
}
194-
}
195188
.onAppear {
196-
if sidebarTabsEnabled {
197-
forceFlatModeIfNeeded()
198-
}
199189
if store.sidebarListMode == .nestedByRepo, sidebarState.expandedRepoIDs.isEmpty {
200190
sidebarState.applyExpandedRepoIDs(
201191
Set(store.sidebarSnapshot.repositories.map(\.id)),
202-
listMode: store.sidebarListMode
192+
listMode: store.sidebarListMode,
193+
alwaysVisibleWorktreePaths: currentAlwaysVisibleWorktreePaths()
203194
)
204195
}
205196
clearSelectionIfMainInFlatMode()
@@ -297,7 +288,7 @@ struct WorktrunkSidebarView: View {
297288
if store.sidebarListMode == .flatWorktrees {
298289
flatWorktreeList(snapshot: snapshot, excludingWorktreePaths: topWorktreePaths)
299290
} else {
300-
nestedRepoList(snapshot: snapshot)
291+
nestedRepoList(snapshot: snapshot, excludingWorktreePaths: topWorktreePaths)
301292
}
302293
}
303294
.background(SidebarListScrollFinder(preserver: sidebarScrollPreserver))
@@ -321,14 +312,11 @@ struct WorktrunkSidebarView: View {
321312
return nil
322313
}
323314

324-
private func forceFlatModeIfNeeded() {
325-
if store.sidebarListMode != .flatWorktrees {
326-
store.sidebarListMode = .flatWorktrees
327-
}
328-
if store.worktreeSortOrder != .recentActivity {
329-
store.worktreeSortOrder = .recentActivity
330-
}
331-
clearSelectionIfMainInFlatMode()
315+
private func currentAlwaysVisibleWorktreePaths() -> Set<String> {
316+
guard sidebarTabsEnabled else { return [] }
317+
guard store.sidebarListMode == .nestedByRepo else { return [] }
318+
let tabRoots = Set(openTabsModel.tabs.compactMap(\.worktreeRootPath).map(standardizedPath))
319+
return tabRoots.intersection(store.sidebarWorktreePaths)
332320
}
333321

334322
@ViewBuilder
@@ -347,6 +335,7 @@ struct WorktrunkSidebarView: View {
347335
return (key, item.tab.windowNumber)
348336
}
349337
)
338+
let alwaysVisibleWorktreePaths = Set(windowNumberByWorktreePath.keys)
350339
let moveBeforePreservingScroll: (Int, Int) -> Void = { moving, target in
351340
let scrollY = sidebarScrollPreserver.captureScrollY()
352341
moveNativeTabBefore(moving, target)
@@ -379,6 +368,7 @@ struct WorktrunkSidebarView: View {
379368
openWorktreeAgent: openWorktreeAgent,
380369
defaultAction: defaultAction,
381370
availableAgents: availableAgents,
371+
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths,
382372
focusNativeTab: focusNativeTab,
383373
moveBefore: moveBeforePreservingScroll,
384374
moveAfter: moveAfterPreservingScroll,
@@ -419,7 +409,10 @@ struct WorktrunkSidebarView: View {
419409
}
420410

421411
@ViewBuilder
422-
private func nestedRepoList(snapshot: WorktrunkStore.SidebarSnapshot) -> some View {
412+
private func nestedRepoList(
413+
snapshot: WorktrunkStore.SidebarSnapshot,
414+
excludingWorktreePaths: Set<String>
415+
) -> some View {
423416
ForEach(snapshot.repositories) { repo in
424417
DisclosureGroup(
425418
isExpanded: Binding(
@@ -431,7 +424,11 @@ struct WorktrunkSidebarView: View {
431424
} else {
432425
next.remove(repo.id)
433426
}
434-
sidebarState.applyExpandedRepoIDs(next, listMode: store.sidebarListMode)
427+
sidebarState.applyExpandedRepoIDs(
428+
next,
429+
listMode: store.sidebarListMode,
430+
alwaysVisibleWorktreePaths: excludingWorktreePaths
431+
)
435432
}
436433
)
437434
) {
@@ -449,7 +446,9 @@ struct WorktrunkSidebarView: View {
449446
}
450447
.help("Create worktree")
451448

452-
let worktrees = snapshot.worktreesByRepositoryID[repo.id] ?? []
449+
let worktrees = (snapshot.worktreesByRepositoryID[repo.id] ?? []).filter { wt in
450+
!excludingWorktreePaths.contains(standardizedPath(wt.path))
451+
}
453452
if worktrees.isEmpty {
454453
Text("No worktrees")
455454
.foregroundStyle(.secondary)
@@ -601,7 +600,11 @@ struct WorktrunkSidebarView: View {
601600
} else {
602601
next.remove(wt.path)
603602
}
604-
sidebarState.applyExpandedWorktreePaths(next, listMode: store.sidebarListMode)
603+
sidebarState.applyExpandedWorktreePaths(
604+
next,
605+
listMode: store.sidebarListMode,
606+
alwaysVisibleWorktreePaths: currentAlwaysVisibleWorktreePaths()
607+
)
605608
}
606609
)
607610
) {
@@ -867,6 +870,7 @@ private struct WorktreeTabDisclosureGroup: View {
867870
let openWorktreeAgent: (String, WorktrunkAgent) -> Void
868871
let defaultAction: WorktrunkDefaultAction
869872
let availableAgents: [WorktrunkAgent]
873+
let alwaysVisibleWorktreePaths: Set<String>
870874
let focusNativeTab: (Int) -> Void
871875
let moveBefore: (Int, Int) -> Void
872876
let moveAfter: (Int, Int) -> Void
@@ -883,7 +887,11 @@ private struct WorktreeTabDisclosureGroup: View {
883887
} else {
884888
next.remove(worktree.path)
885889
}
886-
sidebarState.applyExpandedWorktreePaths(next, listMode: store.sidebarListMode)
890+
sidebarState.applyExpandedWorktreePaths(
891+
next,
892+
listMode: store.sidebarListMode,
893+
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths
894+
)
887895
}
888896
)
889897
) {

0 commit comments

Comments
 (0)