Skip to content

Commit bb6e7a6

Browse files
authored
Add sidebar tab context menu actions (#32)
* Add sidebar tab context menu actions * Fix sidebar tab close in single-window mode
1 parent db81d08 commit bb6e7a6

4 files changed

Lines changed: 58 additions & 16 deletions

File tree

macos/Sources/Features/Terminal/TerminalController.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,14 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
895895
}
896896
}
897897

898+
private func closeNativeTab(windowNumber: Int) {
899+
guard let window else { return }
900+
let candidateWindows = window.tabGroup?.windows ?? [window]
901+
guard let targetWindow = candidateWindows.first(where: { $0.windowNumber == windowNumber }) else { return }
902+
guard let targetController = targetWindow.windowController as? TerminalController else { return }
903+
targetController.closeTab(nil)
904+
}
905+
898906
private func moveNativeTabBefore(movingWindowNumber: Int, targetWindowNumber: Int) {
899907
guard movingWindowNumber != targetWindowNumber else { return }
900908
guard let window else { return }
@@ -1458,6 +1466,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
14581466
focusNativeTab: { [weak self] windowNumber in
14591467
self?.focusNativeTab(windowNumber: windowNumber)
14601468
},
1469+
closeNativeTab: { [weak self] windowNumber in
1470+
self?.closeNativeTab(windowNumber: windowNumber)
1471+
},
14611472
moveNativeTabBefore: { [weak self] moving, target in
14621473
self?.moveNativeTabBefore(movingWindowNumber: moving, targetWindowNumber: target)
14631474
},

macos/Sources/Features/Terminal/TerminalWorkspaceView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct TerminalWorkspaceView<ViewModel: TerminalViewModel>: View {
1414
let openWorktreeAgent: (String, WorktrunkAgent) -> Void
1515
let resumeSession: ((AISession) -> Void)?
1616
let focusNativeTab: (Int) -> Void
17+
let closeNativeTab: (Int) -> Void
1718
let moveNativeTabBefore: (Int, Int) -> Void
1819
let moveNativeTabAfter: (Int, Int) -> Void
1920
let onSidebarWidthChange: (CGFloat) -> Void
@@ -36,6 +37,7 @@ struct TerminalWorkspaceView<ViewModel: TerminalViewModel>: View {
3637
openWorktreeAgent: openWorktreeAgent,
3738
resumeSession: resumeSession,
3839
focusNativeTab: focusNativeTab,
40+
closeNativeTab: closeNativeTab,
3941
moveNativeTabBefore: moveNativeTabBefore,
4042
moveNativeTabAfter: moveNativeTabAfter,
4143
onSelectWorktree: { path in

macos/Sources/Features/Terminal/TerminalWorkspaceViewContainer.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class TerminalWorkspaceViewContainer<ViewModel: TerminalViewModel>: NSView {
2020
openWorktreeAgent: @escaping (String, WorktrunkAgent) -> Void,
2121
resumeSession: ((AISession) -> Void)? = nil,
2222
focusNativeTab: @escaping (Int) -> Void,
23+
closeNativeTab: @escaping (Int) -> Void,
2324
moveNativeTabBefore: @escaping (Int, Int) -> Void,
2425
moveNativeTabAfter: @escaping (Int, Int) -> Void,
2526
onSidebarWidthChange: @escaping (CGFloat) -> Void,
@@ -38,6 +39,7 @@ class TerminalWorkspaceViewContainer<ViewModel: TerminalViewModel>: NSView {
3839
openWorktreeAgent: openWorktreeAgent,
3940
resumeSession: resumeSession,
4041
focusNativeTab: focusNativeTab,
42+
closeNativeTab: closeNativeTab,
4143
moveNativeTabBefore: moveNativeTabBefore,
4244
moveNativeTabAfter: moveNativeTabAfter,
4345
onSidebarWidthChange: onSidebarWidthChange,

macos/Sources/Features/Worktrunk/WorktrunkSidebarView.swift

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct WorktrunkSidebarView: View {
1111
let openWorktreeAgent: (String, WorktrunkAgent) -> Void
1212
var resumeSession: ((AISession) -> Void)?
1313
let focusNativeTab: (Int) -> Void
14+
let closeNativeTab: (Int) -> Void
1415
let moveNativeTabBefore: (Int, Int) -> Void
1516
let moveNativeTabAfter: (Int, Int) -> Void
1617
var onSelectWorktree: ((String?) -> Void)?
@@ -367,23 +368,27 @@ struct WorktrunkSidebarView: View {
367368
defaultAction: defaultAction,
368369
availableAgents: availableAgents,
369370
alwaysVisibleWorktreePaths: alwaysVisibleWorktreePaths,
370-
focusNativeTab: focusNativeTab,
371-
moveBefore: moveBeforePreservingScroll,
372-
moveAfter: moveAfterPreservingScroll,
373-
windowNumberByWorktreePath: windowNumberByWorktreePath
374-
)
375-
}
371+
focusNativeTab: focusNativeTab,
372+
closeNativeTab: closeNativeTab,
373+
onRemoveWorktree: { worktree in
374+
removeWorktreeConfirm = worktree
375+
},
376+
moveBefore: moveBeforePreservingScroll,
377+
moveAfter: moveAfterPreservingScroll,
378+
windowNumberByWorktreePath: windowNumberByWorktreePath
379+
)
380+
}
376381

377-
if let last = shownTabs.last?.tab {
378-
Rectangle()
379-
.fill(Color.clear)
380-
.frame(maxWidth: .infinity)
381-
.frame(height: 1)
382-
.contentShape(Rectangle())
383-
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
384-
.listRowSeparator(.hidden)
385-
.overlay(alignment: .center) {
386-
if sidebarTabsEndDropTarget {
382+
if let last = shownTabs.last?.tab {
383+
Rectangle()
384+
.fill(Color.clear)
385+
.frame(maxWidth: .infinity)
386+
.frame(height: 1)
387+
.contentShape(Rectangle())
388+
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
389+
.listRowSeparator(.hidden)
390+
.overlay(alignment: .center) {
391+
if sidebarTabsEndDropTarget {
387392
SidebarInsertionIndicatorLine()
388393
}
389394
}
@@ -870,6 +875,8 @@ private struct WorktreeTabDisclosureGroup: View {
870875
let availableAgents: [WorktrunkAgent]
871876
let alwaysVisibleWorktreePaths: Set<String>
872877
let focusNativeTab: (Int) -> Void
878+
let closeNativeTab: (Int) -> Void
879+
let onRemoveWorktree: (WorktrunkStore.Worktree) -> Void
873880
let moveBefore: (Int, Int) -> Void
874881
let moveAfter: (Int, Int) -> Void
875882
let windowNumberByWorktreePath: [String: Int]
@@ -926,6 +933,12 @@ private struct WorktreeTabDisclosureGroup: View {
926933
sidebarState.selection = .worktree(repoID: worktree.repositoryID, path: worktree.path)
927934
focusNativeTab(tab.windowNumber)
928935
},
936+
onClose: {
937+
closeNativeTab(tab.windowNumber)
938+
},
939+
onRemoveWorktree: {
940+
onRemoveWorktree(worktree)
941+
},
929942
onDropBefore: { moving in
930943
guard moving != tab.windowNumber else { return }
931944
moveBefore(moving, tab.windowNumber)
@@ -951,6 +964,8 @@ private struct WorktreeTabRowLabel: View {
951964
let openWorktree: (String) -> Void
952965
let openWorktreeAgent: (String, WorktrunkAgent) -> Void
953966
let onActivate: () -> Void
967+
let onClose: () -> Void
968+
let onRemoveWorktree: () -> Void
954969
let onDropBefore: (Int) -> Void
955970
let windowNumberByWorktreePath: [String: Int]
956971

@@ -1085,6 +1100,18 @@ private struct WorktreeTabRowLabel: View {
10851100
SidebarInsertionIndicatorLine()
10861101
}
10871102
}
1103+
.contextMenu {
1104+
Button("Close Tab") {
1105+
onClose()
1106+
}
1107+
Button("Remove Worktree…") {
1108+
onRemoveWorktree()
1109+
}
1110+
.disabled(worktree.isMain)
1111+
Button("Reveal in Finder") {
1112+
NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: worktree.path)])
1113+
}
1114+
}
10881115
.onDrop(of: [UTType.fileURL.identifier], isTargeted: isDropTargetBinding) { providers in
10891116
return SidebarFileURLDrop.loadURL(from: providers) { url in
10901117
guard let url else { return }

0 commit comments

Comments
 (0)