native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306import AppKitimport SwiftUI
extension CanvasView { /// Run a reducer-driven canvas command (from the command palette) and clear /// the one-shot request. func fulfillCommandRequest(_ request: CanvasCommandRequest?) { guard let request else { return } switch request.command { case .toggleExpand: toggleExpandFocusedCard() case .arrange: arrangeCardsWithFit() case .organize: organizeCardsWithFit() case .tile: tileCardsWithFit() case .selectAll: selectAllCards() case .navigate(let direction): navigateCard(direction) } onCommandConsumed(request.id) }
/// Expand a card in place: raise it to the top, then flip `expandedTabID` /// inside one `withAnimation` so the card's size, center, and scale all /// interpolate together from its in-canvas frame to the full viewport at /// scale 1 — a magic-move from where the card actually sits. The canvas /// transform is left untouched, so every other card stays exactly where it /// was, behind a dimming scrim. func expandCard(_ tabID: TerminalTabID, states: [WorktreeTerminalState]) { guard viewportSize.width > 0, viewportSize.height > 0, layoutStore.cardLayouts[tabID.rawValue.uuidString] != nil else { return } focusSingleCard(tabID, states: states) withAnimation(expandAnimation) { expandedTabID = tabID } }
/// Restore the expanded card back into the (unchanged) canvas. func collapseExpand() { guard expandedTabID != nil else { return } withAnimation(expandAnimation) { expandedTabID = nil } }
/// Drop expand state without animation — used right before a relayout /// (Arrange/Organize) takes over the canvas. func cancelExpandForRelayout() { expandedTabID = nil }
func fulfillPendingFocusRequest( _ request: CanvasFocusRequest?, states: [WorktreeTerminalState] ) { guard let request else { return } let tabID = CanvasFocusResolver.resolve( request: request, candidates: collectFocusCandidates(from: states), currentPrimaryTabID: selectionState.primaryTabID ) guard let tabID else { return } focusSingleCard(tabID, states: states) focusViewport(on: tabID) onFocusRequestConsumed(request.id) }
func focusViewport(on tabID: TerminalTabID) { guard viewportSize.width > 0, viewportSize.height > 0 else { return } let cardKey = tabID.rawValue.uuidString guard let layout = layoutStore.cardLayouts[cardKey] else { return }
let horizontalPadding: CGFloat = 80 let verticalPadding: CGFloat = 80 + bottomToolbarReserve let availableWidth = max(1, viewportSize.width - horizontalPadding) let availableHeight = max(1, viewportSize.height - verticalPadding) let targetScale = max( 0.25, min( 1.25, min( availableWidth / layout.size.width, availableHeight / (layout.size.height + titleBarHeight) ) ) ) let targetOffset = CGSize( width: viewportSize.width / 2 - layout.position.x * targetScale, height: (viewportSize.height - bottomToolbarReserve) / 2 - layout.position.y * targetScale ) let start = CanvasViewportAnimator.Snapshot(offset: canvasOffset, scale: canvasScale) let end = CanvasViewportAnimator.Snapshot(offset: targetOffset, scale: targetScale) viewportAnimator.animate(from: start, to: end) { [self] snapshot in canvasOffset = snapshot.offset lastCanvasOffset = snapshot.offset canvasScale = snapshot.scale lastCanvasScale = snapshot.scale } }
func handleSelectionShieldTap( _ tabID: TerminalTabID, surfaceState _: WorktreeTerminalState, states: [WorktreeTerminalState] ) { let cmdHeld = NSEvent.modifierFlags.contains(.command) mutateSelection(states: states) { state in if cmdHeld { state.toggleSelection(tabID) } else if state.isBroadcasting, state.selectedTabIDs.contains(tabID) { state.setPrimary(tabID) } else { state.focusSingle(tabID) } } }
func clearSelection(states: [WorktreeTerminalState]) { mutateSelection(states: states) { state in state.clear() } }
func pruneSelection( previousOrder: [TerminalTabID], currentOrder: [TerminalTabID], states: [WorktreeTerminalState] ) { let previousPrimaryTabID = selectionState.primaryTabID selectionState.pruneAutoAdvancingPrimary(previousOrder: previousOrder, currentOrder: currentOrder) syncPrimaryFocus(from: previousPrimaryTabID, to: selectionState.primaryTabID, states: states) syncBroadcastCallbacks(states: states) }
func mutateSelection( states: [WorktreeTerminalState], mutation: (inout CanvasSelectionState) -> Void ) { let previousPrimaryTabID = selectionState.primaryTabID mutation(&selectionState) selectionState.prune(to: Set(collectVisibleTabIDs(from: states))) syncPrimaryFocus(from: previousPrimaryTabID, to: selectionState.primaryTabID, states: states) syncBroadcastCallbacks(states: states) }
func syncPrimaryFocus( from previousTabID: TerminalTabID?, to newTabID: TerminalTabID?, states: [WorktreeTerminalState] ) { if let previousTabID, previousTabID != newTabID { unfocusTab(previousTabID, states: states) }
guard let newTabID, let ownerState = states.first(where: { $0.surfaceView(for: newTabID) != nil }), let surfaceView = ownerState.surfaceView(for: newTabID) else { terminalManager.canvasFocusedWorktreeID = nil onFocusedWorktreeChanged(nil) return }
layoutStore.moveToFront(newTabID.rawValue.uuidString) ownerState.tabManager.selectTab(newTabID) terminalManager.canvasFocusedWorktreeID = ownerState.worktreeID onFocusedWorktreeChanged(ownerState.worktreeID) surfaceView.focusDidChange(true) surfaceView.requestFocus() }
func unfocusTab(_ tabID: TerminalTabID, states: [WorktreeTerminalState]) { guard let state = states.first(where: { $0.surfaceView(for: tabID) != nil }) else { return } for surface in state.splitTree(for: tabID).leaves() { surface.focusDidChange(false) } }
func syncBroadcastCallbacks(states: [WorktreeTerminalState]) { clearBroadcastCallbacks(states: states)
guard selectionState.isBroadcasting, let primaryTabID = selectionState.primaryTabID, let primaryState = terminalManager.stateContaining(tabId: primaryTabID) else { return }
let selectedTabIDs = selectionState.selectedTabIDs let beginBroadcast = { selectionState.beginBroadcastInteractionIfNeeded() } for primarySurface in primaryState.splitTree(for: primaryTabID).leaves() { primarySurface.onCommittedText = { [terminalManager, selectedTabIDs, primaryTabID, beginBroadcast] text in Task { @MainActor in beginBroadcast() terminalManager.broadcastCommittedText(text, from: primaryTabID, to: selectedTabIDs) } } primarySurface.onMirroredKey = { [terminalManager, selectedTabIDs, primaryTabID, beginBroadcast] mirroredKey in Task { @MainActor in beginBroadcast() terminalManager.broadcastMirroredKey(mirroredKey, from: primaryTabID, to: selectedTabIDs) } } } }
func clearBroadcastCallbacks(states: [WorktreeTerminalState]) { for state in states { for tab in state.tabManager.tabs { for surface in state.splitTree(for: tab.id).leaves() { surface.onCommittedText = nil surface.onMirroredKey = nil } } } }
// MARK: - Occlusion
func activateCanvas() { cleanStaleLayouts()
let activeStates = terminalManager.activeWorktreeStates
// Mark all states as canvas-managed so that tree updates (e.g. split // creation) don't trigger applySurfaceActivity with stale normal-mode // window visibility, which would occlude every surface. for state in activeStates { state.isCanvasManaged = true }
// Auto-focus the card that was active before entering canvas. if let selectedID = terminalManager.selectedWorktreeID, let state = activeStates.first(where: { $0.worktreeID == selectedID }), let tabID = state.tabManager.selectedTabId { selectionState.focusSingle(tabID) syncPrimaryFocus(from: nil, to: tabID, states: activeStates) } else { selectionState.clear() syncBroadcastCallbacks(states: activeStates) }
for state in activeStates { state.setAllSurfacesOccluded() } // Un-occlude all surfaces visible on canvas (including split panes) for state in activeStates { for tab in state.tabManager.tabs { for surface in state.splitTree(for: tab.id).leaves() { surface.setOcclusion(true) } } } }
func deactivateCanvas() { viewportAnimator.cancel() expandedTabID = nil let activeStates = terminalManager.activeWorktreeStates for state in activeStates { state.isCanvasManaged = false } clearBroadcastCallbacks(states: activeStates) selectionState.clear() // Don't occlude surfaces here. In SwiftUI's if/else view swap, // onAppear fires before onDisappear, so occluding here would undo // WorktreeTerminalTabsView.onAppear's syncFocus() and cause blank // surfaces. Cleanup of non-selected worktrees is handled by // setSelectedWorktreeID in the async exit flow. }
/// Looks up the user-pinned `RepositoryAppearance` for a given repo /// root URL by deriving the canonical `Repository.ID` (the /// path-policy-normalized path string) and querying the @Shared /// dict. Returns `.empty` when no entry exists, which keeps cards /// visually identical to before the appearance feature shipped. func appearance(for repositoryRootURL: URL) -> RepositoryAppearance { let id = repositoryID(for: repositoryRootURL) return repositoryAppearances[id] ?? .empty }
/// Resolves the user-defined display title for the repo at this root /// URL, falling back to `Repository.name(for:)` (folder name) when no /// custom title was set. Reads from the static dictionary populated /// by the parent reducer — no per-call `@Shared` subscription on the /// canvas hot path. func repositoryDisplayName(for repositoryRootURL: URL) -> String { let id = repositoryID(for: repositoryRootURL) return repositoryCustomTitles[id] ?? Repository.name(for: repositoryRootURL) }
/// Mirrors the same path normalization the `Repository.ID` is built /// from, so dict lookups match what the reducer stores. func repositoryID(for repositoryRootURL: URL) -> Repository.ID { PathPolicy.normalizePath( repositoryRootURL.path(percentEncoded: false), resolvingSymlinks: true ) ?? repositoryRootURL.path(percentEncoded: false) }}