native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #66 from supabitapp/fix-sidebar-arrow-focus

Respect sidebar focus, auto-focus new worktrees

authored by

khoi and committed by
GitHub
33edecc2 8bad4750

+44 -8
+1 -1
AGENTS.md
··· 110 110 - To inspect a Swift PM package, clone it with `gj get {git_url}` 111 111 - Automatically commit your changes and your changes only. Do not use `git add .` 112 112 - Never mention competitors or other apps in commits or PRs 113 - - Before you go on your task, check the current git branch name, if it's not descriptive enough, name it accordingly 113 + - Before you go on your task, check the current git branch name, if it's something generic like an animal name, name it accordingly. Do not do this for main branch 114 114 115 115 ## References 116 116
+17
supacode/Features/Repositories/Reducer/RepositoriesFeature.swift
··· 15 15 var isOpenPanelPresented = false 16 16 var pendingWorktrees: [PendingWorktree] = [] 17 17 var pendingSetupScriptWorktreeIDs: Set<Worktree.ID> = [] 18 + var pendingTerminalFocusWorktreeIDs: Set<Worktree.ID> = [] 18 19 var deletingWorktreeIDs: Set<Worktree.ID> = [] 19 20 var removingRepositoryIDs: Set<Repository.ID> = [] 20 21 var pinnedWorktreeIDs: [Worktree.ID] = [] ··· 46 47 previousSelection: Worktree.ID? 47 48 ) 48 49 case consumeSetupScript(Worktree.ID) 50 + case consumeTerminalFocus(Worktree.ID) 49 51 case requestRemoveWorktree(Worktree.ID, Repository.ID) 50 52 case presentWorktreeRemovalConfirmation(Worktree.ID, Repository.ID) 51 53 case removeWorktreeConfirmed(Worktree.ID, Repository.ID) ··· 272 274 let pendingID 273 275 ): 274 276 state.pendingSetupScriptWorktreeIDs.insert(worktree.id) 277 + state.pendingTerminalFocusWorktreeIDs.insert(worktree.id) 275 278 removePendingWorktree(pendingID, state: &state) 276 279 if state.selectedWorktreeID == pendingID { 277 280 state.selectedWorktreeID = worktree.id ··· 297 300 state.pendingSetupScriptWorktreeIDs.remove(id) 298 301 return .none 299 302 303 + case .consumeTerminalFocus(let id): 304 + state.pendingTerminalFocusWorktreeIDs.remove(id) 305 + return .none 306 + 300 307 case .requestRemoveWorktree(let worktreeID, let repositoryID): 301 308 if state.removingRepositoryIDs.contains(repositoryID) { 302 309 return .none ··· 385 392 let nextSelection 386 393 ): 387 394 state.pendingSetupScriptWorktreeIDs.remove(worktreeID) 395 + state.pendingTerminalFocusWorktreeIDs.remove(worktreeID) 388 396 let roots = state.repositories.map(\.rootURL) 389 397 if selectionWasRemoved { 390 398 state.selectedWorktreeID = ··· 527 535 let filteredSetupScriptIDs = state.pendingSetupScriptWorktreeIDs.filter { 528 536 availableWorktreeIDs.contains($0) 529 537 } 538 + let filteredFocusIDs = state.pendingTerminalFocusWorktreeIDs.filter { 539 + availableWorktreeIDs.contains($0) 540 + } 530 541 if animated { 531 542 withAnimation { 532 543 state.repositories = repositories 533 544 state.pendingWorktrees = filteredPendingWorktrees 534 545 state.deletingWorktreeIDs = filteredDeletingIDs 535 546 state.pendingSetupScriptWorktreeIDs = filteredSetupScriptIDs 547 + state.pendingTerminalFocusWorktreeIDs = filteredFocusIDs 536 548 } 537 549 } else { 538 550 state.repositories = repositories 539 551 state.pendingWorktrees = filteredPendingWorktrees 540 552 state.deletingWorktreeIDs = filteredDeletingIDs 541 553 state.pendingSetupScriptWorktreeIDs = filteredSetupScriptIDs 554 + state.pendingTerminalFocusWorktreeIDs = filteredFocusIDs 542 555 } 543 556 if prunePinnedWorktreeIDs(state: &state) { 544 557 repositoryPersistence.savePinnedWorktreeIDs(state.pinnedWorktreeIDs) ··· 613 626 func pendingWorktree(for id: Worktree.ID?) -> PendingWorktree? { 614 627 guard let id else { return nil } 615 628 return pendingWorktrees.first(where: { $0.id == id }) 629 + } 630 + 631 + func shouldFocusTerminal(for worktreeID: Worktree.ID) -> Bool { 632 + pendingTerminalFocusWorktreeIDs.contains(worktreeID) 616 633 } 617 634 618 635 func selectedRow(for id: Worktree.ID?) -> WorktreeRowModel? {
+5
supacode/Features/Repositories/Views/WorktreeDetailView.swift
··· 35 35 WorktreeLoadingView(info: loadingInfo) 36 36 } else if let selectedWorktree { 37 37 let shouldRunSetupScript = repositories.pendingSetupScriptWorktreeIDs.contains(selectedWorktree.id) 38 + let shouldFocusTerminal = repositories.shouldFocusTerminal(for: selectedWorktree.id) 38 39 WorktreeTerminalTabsView( 39 40 worktree: selectedWorktree, 40 41 manager: terminalManager, 41 42 shouldRunSetupScript: shouldRunSetupScript, 43 + forceAutoFocus: shouldFocusTerminal, 42 44 createTab: { store.send(.newTerminal) } 43 45 ) 44 46 .id(selectedWorktree.id) ··· 46 48 .onAppear { 47 49 if shouldRunSetupScript { 48 50 store.send(.repositories(.consumeSetupScript(selectedWorktree.id))) 51 + } 52 + if shouldFocusTerminal { 53 + store.send(.repositories(.consumeTerminalFocus(selectedWorktree.id))) 49 54 } 50 55 } 51 56 } else {
+4 -4
supacode/Features/Terminal/Models/WorktreeTerminalState.swift
··· 42 42 return .idle 43 43 } 44 44 45 - func ensureInitialTab() { 45 + func ensureInitialTab(focusing: Bool) { 46 46 if tabManager.tabs.isEmpty { 47 - _ = createTab() 47 + _ = createTab(focusing: focusing) 48 48 } 49 49 } 50 50 51 51 @discardableResult 52 - func createTab() -> TerminalTabID? { 52 + func createTab(focusing: Bool = true) -> TerminalTabID? { 53 53 let title = "\(worktree.name) \(nextTabIndex())" 54 54 let tabId = tabManager.createTab(title: title, icon: "terminal") 55 55 let resolvedInput = setupScriptInput(shouldRun: pendingSetupScript) ··· 58 58 } 59 59 let tree = splitTree(for: tabId, initialInput: resolvedInput) 60 60 tabIsRunningById[tabId] = false 61 - if let surface = tree.root?.leftmostLeaf() { 61 + if focusing, let surface = tree.root?.leftmostLeaf() { 62 62 focusSurface(surface, in: tabId) 63 63 } 64 64 onTabCreated?()
+17 -3
supacode/Features/Terminal/Views/WorktreeTerminalTabsView.swift
··· 1 + import AppKit 1 2 import SwiftUI 2 3 3 4 struct WorktreeTerminalTabsView: View { 4 5 let worktree: Worktree 5 6 let manager: WorktreeTerminalManager 6 7 let shouldRunSetupScript: Bool 8 + let forceAutoFocus: Bool 7 9 let createTab: () -> Void 8 10 9 11 var body: some View { ··· 36 38 } 37 39 } 38 40 .onAppear { 39 - state.ensureInitialTab() 40 - state.focusSelectedTab() 41 + state.ensureInitialTab(focusing: false) 42 + if shouldAutoFocusTerminal { 43 + state.focusSelectedTab() 44 + } 41 45 } 42 46 .onChange(of: state.tabManager.selectedTabId) { _, _ in 43 - state.focusSelectedTab() 47 + if shouldAutoFocusTerminal { 48 + state.focusSelectedTab() 49 + } 44 50 } 51 + } 52 + 53 + private var shouldAutoFocusTerminal: Bool { 54 + if forceAutoFocus { 55 + return true 56 + } 57 + guard let responder = NSApp.keyWindow?.firstResponder else { return true } 58 + return !(responder is NSTableView) && !(responder is NSOutlineView) 45 59 } 46 60 }