@@ -800,15 +800,24 @@ class TabManager: ObservableObject {
800800 private var pendingWorkspaceUnfocusTarget : ( tabId: UUID , panelId: UUID ) ?
801801 private var sidebarSelectedWorkspaceIds : Set < UUID > = [ ]
802802 var confirmCloseHandler : ( ( String , String , Bool ) -> Bool ) ?
803- private struct WorkspaceCreationSnapshot {
804- let tabs : [ Workspace ]
805- let selectedTabId : UUID ?
803+ private struct WorkspaceCreationTabSnapshot {
804+ let id : UUID
805+ let isPinned : Bool
806806
807- var selectedWorkspace : Workspace ? {
808- guard let selectedTabId else { return nil }
809- return tabs. first ( where: { $0. id == selectedTabId } )
807+ @MainActor
808+ init ( workspace: Workspace ) {
809+ self . id = workspace. id
810+ self . isPinned = workspace. isPinned
810811 }
811812 }
813+
814+ private struct WorkspaceCreationSnapshot {
815+ let tabs : [ WorkspaceCreationTabSnapshot ]
816+ let selectedTabId : UUID ?
817+ let selectedTabWasPinned : Bool
818+ let preferredWorkingDirectory : String ?
819+ let inheritedTerminalConfig : ghostty_surface_config_s ?
820+ }
812821 private var agentPIDSweepTimer : DispatchSourceTimer ?
813822 private var workspaceGitMetadataPollTimer : DispatchSourceTimer ?
814823#if DEBUG
@@ -1176,14 +1185,15 @@ class TabManager: ObservableObject {
11761185 } ( )
11771186 guard isEnabled,
11781187 let selectedTabId = snapshot. selectedTabId,
1179- let target = snapshot. tabs. first ( where: { $0. id != selectedTabId } ) else {
1188+ let targetId = snapshot. tabs. lazy. map ( \. id) . first ( where: { $0 != selectedTabId } ) ,
1189+ tabs. contains ( where: { $0. id == targetId } ) else {
11801190 return
11811191 }
11821192 dlog (
11831193 " workspace.create.devSelectionMutation from= \( selectedTabId. uuidString. prefix ( 5 ) ) " +
1184- " to= \( target . id . uuidString. prefix ( 5 ) ) "
1194+ " to= \( targetId . uuidString. prefix ( 5 ) ) "
11851195 )
1186- self . selectedTabId = target . id
1196+ self . selectedTabId = targetId
11871197 }
11881198#endif
11891199
@@ -1207,8 +1217,8 @@ class TabManager: ObservableObject {
12071217 let nextTabCount = snapshot. tabs. count + 1
12081218 sentryBreadcrumb ( " workspace.create " , data: [ " tabCount " : nextTabCount] )
12091219 let explicitWorkingDirectory = normalizedWorkingDirectory ( overrideWorkingDirectory)
1210- let workingDirectory = explicitWorkingDirectory ?? preferredWorkingDirectoryForNewTab ( snapshot: snapshot )
1211- let inheritedConfig = inheritedTerminalConfigForNewWorkspace ( snapshot: snapshot )
1220+ let workingDirectory = explicitWorkingDirectory ?? snapshot. preferredWorkingDirectory
1221+ let inheritedConfig = snapshot. inheritedTerminalConfig
12121222 // Resolve placement against the pre-creation snapshot before Workspace init
12131223 // boots terminal state. The ssh/new-workspace path can otherwise crash while
12141224 // reading @Published placement state from existing workspaces mid-creation.
@@ -1228,7 +1238,9 @@ class TabManager: ObservableObject {
12281238 if eagerLoadTerminal && !select {
12291239 requestBackgroundWorkspaceLoad ( for: newWorkspace. id)
12301240 }
1231- var updatedTabs = snapshot. tabs
1241+ // Apply insertion to the current live array so post-snapshot closes/reorders
1242+ // are preserved instead of reintroducing stale workspace instances.
1243+ var updatedTabs = tabs
12321244 if insertIndex >= 0 && insertIndex <= updatedTabs. count {
12331245 updatedTabs. insert ( newWorkspace, at: insertIndex)
12341246 } else {
@@ -2160,20 +2172,29 @@ class TabManager: ObservableObject {
21602172 }
21612173
21622174 func terminalPanelForWorkspaceConfigInheritanceSource( ) -> TerminalPanel ? {
2163- terminalPanelForWorkspaceConfigInheritanceSource ( snapshot : workspaceCreationSnapshot ( ) )
2175+ terminalPanelForWorkspaceConfigInheritanceSource ( workspace : selectedWorkspace )
21642176 }
21652177
21662178 private func workspaceCreationSnapshot( ) -> WorkspaceCreationSnapshot {
2167- WorkspaceCreationSnapshot (
2168- tabs: tabs,
2169- selectedTabId: selectedTabId
2179+ let currentTabs = tabs
2180+ let currentSelectedTabId = selectedTabId
2181+ let selectedWorkspace = currentSelectedTabId. flatMap { selectedTabId in
2182+ currentTabs. first ( where: { $0. id == selectedTabId } )
2183+ }
2184+
2185+ return WorkspaceCreationSnapshot (
2186+ tabs: currentTabs. map { WorkspaceCreationTabSnapshot ( workspace: $0) } ,
2187+ selectedTabId: currentSelectedTabId,
2188+ selectedTabWasPinned: selectedWorkspace? . isPinned ?? false ,
2189+ preferredWorkingDirectory: preferredWorkingDirectoryForNewTab ( workspace: selectedWorkspace) ,
2190+ inheritedTerminalConfig: inheritedTerminalConfigForNewWorkspace ( workspace: selectedWorkspace)
21702191 )
21712192 }
21722193
21732194 private func terminalPanelForWorkspaceConfigInheritanceSource(
2174- snapshot : WorkspaceCreationSnapshot
2195+ workspace : Workspace ?
21752196 ) -> TerminalPanel ? {
2176- guard let workspace = snapshot . selectedWorkspace else { return nil }
2197+ guard let workspace else { return nil }
21772198 if let focusedTerminal = workspace. focusedTerminalPanel {
21782199 return focusedTerminal
21792200 }
@@ -2188,21 +2209,21 @@ class TabManager: ObservableObject {
21882209 }
21892210
21902211 private func inheritedTerminalConfigForNewWorkspace( ) -> ghostty_surface_config_s ? {
2191- inheritedTerminalConfigForNewWorkspace ( snapshot : workspaceCreationSnapshot ( ) )
2212+ inheritedTerminalConfigForNewWorkspace ( workspace : selectedWorkspace )
21922213 }
21932214
21942215 private func inheritedTerminalConfigForNewWorkspace(
2195- snapshot : WorkspaceCreationSnapshot
2216+ workspace : Workspace ?
21962217 ) -> ghostty_surface_config_s ? {
2197- if let panel = terminalPanelForWorkspaceConfigInheritanceSource ( snapshot : snapshot ) ,
2218+ if let panel = terminalPanelForWorkspaceConfigInheritanceSource ( workspace : workspace ) ,
21982219 panel. surface. hasLiveSurface,
21992220 let sourceSurface = panel. surface. surface {
22002221 return cmuxInheritedSurfaceConfig (
22012222 sourceSurface: sourceSurface,
22022223 context: GHOSTTY_SURFACE_CONTEXT_TAB
22032224 )
22042225 }
2205- if let fallbackFontPoints = snapshot . selectedWorkspace ? . lastRememberedTerminalFontPointsForConfigInheritance ( ) {
2226+ if let fallbackFontPoints = workspace ? . lastRememberedTerminalFontPointsForConfigInheritance ( ) {
22062227 var config = ghostty_surface_config_new ( )
22072228 config. font_size = fallbackFontPoints
22082229 return config
@@ -2226,44 +2247,46 @@ class TabManager: ObservableObject {
22262247 placementOverride: NewWorkspacePlacement ? = nil
22272248 ) -> Int {
22282249 let placement = placementOverride ?? WorkspacePlacementSettings . current ( )
2229- let tabs = snapshot. tabs
2230- var pinnedCount = 0
2231- var selectedIndex : Int ?
2232- var selectedIsPinned = false
2233- let selectedTabId = snapshot. selectedTabId
2234-
2235- for (index, tab) in tabs. enumerated ( ) {
2250+ let liveTabs = tabs. map { WorkspaceCreationTabSnapshot ( workspace: $0) }
2251+ let pinnedCount = liveTabs. reduce ( into: 0 ) { partial, tab in
22362252 if tab. isPinned {
2237- pinnedCount += 1
2238- }
2239- if selectedIndex == nil , tab. id == selectedTabId {
2240- selectedIndex = index
2241- selectedIsPinned = tab. isPinned
2253+ partial += 1
22422254 }
22432255 }
22442256
2245- return WorkspacePlacementSettings . insertionIndex (
2246- placement: placement,
2247- selectedIndex: selectedIndex,
2248- selectedIsPinned: selectedIsPinned,
2249- pinnedCount: pinnedCount,
2250- totalCount: tabs. count
2251- )
2257+ switch placement {
2258+ case . top:
2259+ return pinnedCount
2260+ case . end:
2261+ return liveTabs. count
2262+ case . afterCurrent:
2263+ if let selectedTabId = snapshot. selectedTabId,
2264+ let selectedIndex = liveTabs. firstIndex ( where: { $0. id == selectedTabId } ) {
2265+ return WorkspacePlacementSettings . insertionIndex (
2266+ placement: placement,
2267+ selectedIndex: selectedIndex,
2268+ selectedIsPinned: snapshot. selectedTabWasPinned,
2269+ pinnedCount: pinnedCount,
2270+ totalCount: liveTabs. count
2271+ )
2272+ }
2273+ return snapshot. selectedTabWasPinned ? pinnedCount : liveTabs. count
2274+ }
22522275 }
22532276
22542277 private func preferredWorkingDirectoryForNewTab( ) -> String ? {
2255- preferredWorkingDirectoryForNewTab ( snapshot : workspaceCreationSnapshot ( ) )
2278+ preferredWorkingDirectoryForNewTab ( workspace : selectedWorkspace )
22562279 }
22572280
22582281 private func preferredWorkingDirectoryForNewTab(
2259- snapshot : WorkspaceCreationSnapshot
2282+ workspace : Workspace ?
22602283 ) -> String ? {
2261- guard let tab = snapshot . selectedWorkspace else {
2284+ guard let workspace else {
22622285 return nil
22632286 }
2264- let focusedDirectory = tab . focusedPanelId
2265- . flatMap { tab . panelDirectories [ $0] }
2266- let candidate = focusedDirectory ?? tab . currentDirectory
2287+ let focusedDirectory = workspace . focusedPanelId
2288+ . flatMap { workspace . panelDirectories [ $0] }
2289+ let candidate = focusedDirectory ?? workspace . currentDirectory
22672290 let normalized = normalizeDirectory ( candidate)
22682291 let trimmed = normalized. trimmingCharacters ( in: . whitespacesAndNewlines)
22692292 return trimmed. isEmpty ? nil : normalized
0 commit comments