native macOS codings agent orchestrator
6
fork

Configure Feed

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

Merge pull request #132 from onevcat/onevclaw/issue-105-cli-v1-followup

Fix blank terminal after exiting Canvas

authored by

Wei Wang and committed by
GitHub
2aa9b805 1470b6c3

+64 -3
+12 -2
supacode/Features/Terminal/BusinessLogic/WorktreeTerminalManager.swift
··· 121 121 setCommandFinishedNotification(enabled: enabled, threshold: threshold) 122 122 case .setCanvasMode(let enabled): 123 123 if enabled { 124 + terminalLogger.info("[CanvasExit] enteringCanvas previousSelectedWorktree=\(selectedWorktreeID ?? "nil")") 124 125 selectedWorktreeID = nil 125 126 } 126 127 case .setSelectedWorktreeID(let id): 127 128 guard id != selectedWorktreeID else { return } 128 - if let previousID = selectedWorktreeID, let previousState = states[previousID] { 129 + let previousSelectedWorktreeID = selectedWorktreeID 130 + let leavingCanvas = previousSelectedWorktreeID == nil 131 + if let previousID = previousSelectedWorktreeID, let previousState = states[previousID] { 129 132 previousState.setAllSurfacesOccluded() 130 - } else if selectedWorktreeID == nil { 133 + } else if leavingCanvas { 131 134 // Leaving canvas mode: occlude all worktrees except the newly selected one. 132 135 for (wid, state) in states where wid != id { 133 136 state.setAllSurfacesOccluded() 134 137 } 135 138 } 136 139 selectedWorktreeID = id 140 + terminalLogger.info( 141 + "[CanvasExit] setSelectedWorktreeID previous=\(previousSelectedWorktreeID ?? "nil") " 142 + + "next=\(id ?? "nil") leavingCanvas=\(leavingCanvas) states=\(states.count)" 143 + ) 144 + if leavingCanvas, let id, let state = states[id] { 145 + state.refreshSurfaceActivity(reason: "canvas-exit-selected-worktree") 146 + } 137 147 terminalLogger.info("Selected worktree \(id ?? "nil")") 138 148 case .saveLayoutSnapshot: 139 149 terminalLogger.info("[LayoutRestore] received saveLayoutSnapshot command")
+14
supacode/Features/Terminal/Models/WorktreeTerminalState.swift
··· 289 289 applySurfaceActivity() 290 290 } 291 291 292 + func refreshSurfaceActivity(reason: String) { 293 + terminalStateLogger.info( 294 + "[CanvasExit] refreshSurfaceActivity worktree=\(worktree.id) reason=\(reason) " 295 + + "selectedTab=\(tabManager.selectedTabId?.rawValue.uuidString ?? "nil") " 296 + + "windowKey=\(String(describing: lastWindowIsKey)) " 297 + + "windowVisible=\(String(describing: lastWindowIsVisible)) " 298 + + "surfaces=\(surfaces.count) tabs=\(tabManager.tabs.count)" 299 + ) 300 + for surface in surfaces.values { 301 + surface.invalidateOcclusionCache(reason: reason) 302 + } 303 + applySurfaceActivity() 304 + } 305 + 292 306 private func applySurfaceActivity() { 293 307 let selectedTabId = tabManager.selectedTabId 294 308 var surfaceToFocus: GhosttySurfaceView?
+15 -1
supacode/Features/Terminal/Views/WorktreeTerminalTabsView.swift
··· 1 1 import AppKit 2 2 import SwiftUI 3 3 4 + private let terminalTabsLogger = SupaLogger("TerminalTabs") 5 + 4 6 struct WorktreeTerminalTabsView: View { 5 7 let worktree: Worktree 6 8 let manager: WorktreeTerminalManager ··· 57 59 state.focusSelectedTab() 58 60 } 59 61 let activity = resolvedWindowActivity 62 + terminalTabsLogger.info( 63 + "[CanvasExit] onAppear worktree=\(worktree.id) " 64 + + "selectedTab=\(state.tabManager.selectedTabId?.rawValue.uuidString ?? "nil") " 65 + + "autoFocus=\(shouldAutoFocusTerminal) " 66 + + "windowKey=\(activity.isKeyWindow) windowVisible=\(activity.isVisible)" 67 + ) 60 68 state.syncFocus(windowIsKey: activity.isKeyWindow, windowIsVisible: activity.isVisible) 61 69 } 62 - .onChange(of: state.tabManager.selectedTabId) { _, _ in 70 + .onChange(of: state.tabManager.selectedTabId) { _, newValue in 63 71 if shouldAutoFocusTerminal { 64 72 state.focusSelectedTab() 65 73 } 66 74 let activity = resolvedWindowActivity 75 + terminalTabsLogger.info( 76 + "[CanvasExit] selectedTabChanged worktree=\(worktree.id) " 77 + + "selectedTab=\(newValue?.rawValue.uuidString ?? "nil") " 78 + + "autoFocus=\(shouldAutoFocusTerminal) " 79 + + "windowKey=\(activity.isKeyWindow) windowVisible=\(activity.isVisible)" 80 + ) 67 81 state.syncFocus(windowIsKey: activity.isKeyWindow, windowIsVisible: activity.isVisible) 68 82 } 69 83 }
+23
supacode/Infrastructure/Ghostty/GhosttySurfaceView.swift
··· 5 5 import QuartzCore 6 6 import SwiftUI 7 7 8 + private let surfaceLogger = SupaLogger("Surface") 9 + 8 10 final class GhosttySurfaceView: NSView, Identifiable { 9 11 struct OcclusionState { 10 12 private(set) var desired: Bool? ··· 90 92 91 93 private let runtime: GhosttyRuntime 92 94 let id = UUID() 95 + private var debugID: String { 96 + String(id.uuidString.prefix(8)) 97 + } 93 98 let bridge: GhosttySurfaceBridge 94 99 private(set) var surface: ghostty_surface_t? 95 100 private var surfaceRef: GhosttyRuntime.SurfaceReference? ··· 959 964 ghostty_surface_set_occlusion(surface, visible) 960 965 } 961 966 967 + func invalidateOcclusionCache(reason: String) { 968 + surfaceLogger.info( 969 + "[CanvasExit] invalidateOcclusionCache surface=\(debugID) " 970 + + "reason=\(reason) desired=\(String(describing: occlusionState.desired)) " 971 + + "attached=\(superview != nil) window=\(window != nil)" 972 + ) 973 + _ = occlusionState.invalidateForAttachmentChange() 974 + } 975 + 962 976 private func handleAttachmentChange() { 963 977 // Re-parenting can temporarily detach the Metal layer from the visible 964 978 // tree and pause Ghostty's renderer. Invalidate the applied cache so the 965 979 // currently desired occlusion value is sent again after reattachment. 980 + surfaceLogger.info( 981 + "[CanvasExit] attachmentChange surface=\(debugID) " 982 + + "desired=\(String(describing: occlusionState.desired)) " 983 + + "attached=\(superview != nil) window=\(window != nil)" 984 + ) 966 985 _ = occlusionState.invalidateForAttachmentChange() 967 986 guard superview != nil else { return } 968 987 DispatchQueue.main.async { [weak self] in ··· 972 991 973 992 private func reapplyOcclusionIfNeeded() { 974 993 guard superview != nil, let desired = occlusionState.desired else { return } 994 + surfaceLogger.info( 995 + "[CanvasExit] reapplyOcclusion surface=\(debugID) desired=\(desired) " 996 + + "attached=\(superview != nil) window=\(window != nil)" 997 + ) 975 998 setOcclusion(desired) 976 999 } 977 1000