Skip to content

Commit 3bfb7cb

Browse files
committed
Guard nested selection pruning with sidebar tabs
1 parent 6a050e6 commit 3bfb7cb

2 files changed

Lines changed: 63 additions & 14 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: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ struct WorktrunkSidebarView: View {
189189
if store.sidebarListMode == .nestedByRepo, sidebarState.expandedRepoIDs.isEmpty {
190190
sidebarState.applyExpandedRepoIDs(
191191
Set(store.sidebarSnapshot.repositories.map(\.id)),
192-
listMode: store.sidebarListMode
192+
listMode: store.sidebarListMode,
193+
alwaysVisibleWorktreePaths: currentAlwaysVisibleWorktreePaths()
193194
)
194195
}
195196
clearSelectionIfMainInFlatMode()
@@ -311,6 +312,13 @@ struct WorktrunkSidebarView: View {
311312
return nil
312313
}
313314

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)
320+
}
321+
314322
@ViewBuilder
315323
private func sidebarTabsList(
316324
snapshot: WorktrunkStore.SidebarSnapshot,
@@ -327,6 +335,7 @@ struct WorktrunkSidebarView: View {
327335
return (key, item.tab.windowNumber)
328336
}
329337
)
338+
let alwaysVisibleWorktreePaths = Set(windowNumberByWorktreePath.keys)
330339
let moveBeforePreservingScroll: (Int, Int) -> Void = { moving, target in
331340
let scrollY = sidebarScrollPreserver.captureScrollY()
332341
moveNativeTabBefore(moving, target)
@@ -359,6 +368,7 @@ struct WorktrunkSidebarView: View {
359368
openWorktreeAgent: openWorktreeAgent,
360369
defaultAction: defaultAction,
361370
availableAgents: availableAgents,
371+
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths,
362372
focusNativeTab: focusNativeTab,
363373
moveBefore: moveBeforePreservingScroll,
364374
moveAfter: moveAfterPreservingScroll,
@@ -414,7 +424,11 @@ struct WorktrunkSidebarView: View {
414424
} else {
415425
next.remove(repo.id)
416426
}
417-
sidebarState.applyExpandedRepoIDs(next, listMode: store.sidebarListMode)
427+
sidebarState.applyExpandedRepoIDs(
428+
next,
429+
listMode: store.sidebarListMode,
430+
alwaysVisibleWorktreePaths: excludingWorktreePaths
431+
)
418432
}
419433
)
420434
) {
@@ -586,7 +600,11 @@ struct WorktrunkSidebarView: View {
586600
} else {
587601
next.remove(wt.path)
588602
}
589-
sidebarState.applyExpandedWorktreePaths(next, listMode: store.sidebarListMode)
603+
sidebarState.applyExpandedWorktreePaths(
604+
next,
605+
listMode: store.sidebarListMode,
606+
alwaysVisibleWorktreePaths: currentAlwaysVisibleWorktreePaths()
607+
)
590608
}
591609
)
592610
) {
@@ -852,6 +870,7 @@ private struct WorktreeTabDisclosureGroup: View {
852870
let openWorktreeAgent: (String, WorktrunkAgent) -> Void
853871
let defaultAction: WorktrunkDefaultAction
854872
let availableAgents: [WorktrunkAgent]
873+
let alwaysVisibleWorktreePaths: Set<String>
855874
let focusNativeTab: (Int) -> Void
856875
let moveBefore: (Int, Int) -> Void
857876
let moveAfter: (Int, Int) -> Void
@@ -868,7 +887,11 @@ private struct WorktreeTabDisclosureGroup: View {
868887
} else {
869888
next.remove(worktree.path)
870889
}
871-
sidebarState.applyExpandedWorktreePaths(next, listMode: store.sidebarListMode)
890+
sidebarState.applyExpandedWorktreePaths(
891+
next,
892+
listMode: store.sidebarListMode,
893+
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths
894+
)
872895
}
873896
)
874897
) {

0 commit comments

Comments
 (0)