diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index c9b30137..c70d9e53 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -594,15 +594,22 @@ class TerminalSurface: Identifiable { let tabId: UUID private let surfaceContext: ghostty_surface_context_e private let configTemplate: ghostty_surface_config_s? + private let workingDirectory: String? let hostedView: GhosttySurfaceScrollView private let surfaceView: GhosttyNSView private var ownsDisplayLink = false - init(tabId: UUID, context: ghostty_surface_context_e, configTemplate: ghostty_surface_config_s?) { + init( + tabId: UUID, + context: ghostty_surface_context_e, + configTemplate: ghostty_surface_config_s?, + workingDirectory: String? = nil + ) { self.id = UUID() self.tabId = tabId self.surfaceContext = context self.configTemplate = configTemplate + self.workingDirectory = workingDirectory?.trimmingCharacters(in: .whitespacesAndNewlines) let view = GhosttyNSView(frame: .zero) self.surfaceView = view self.hostedView = GhosttySurfaceScrollView(surfaceView: view) @@ -692,15 +699,26 @@ class TerminalSurface: Identifiable { } } - if !envVars.isEmpty { - let envVarsCount = envVars.count - envVars.withUnsafeMutableBufferPointer { buffer in - surfaceConfig.env_vars = buffer.baseAddress - surfaceConfig.env_var_count = envVarsCount - surface = ghostty_surface_new(app, &surfaceConfig) + let createSurface = { [self] in + if !envVars.isEmpty { + let envVarsCount = envVars.count + envVars.withUnsafeMutableBufferPointer { buffer in + surfaceConfig.env_vars = buffer.baseAddress + surfaceConfig.env_var_count = envVarsCount + self.surface = ghostty_surface_new(app, &surfaceConfig) + } + } else { + self.surface = ghostty_surface_new(app, &surfaceConfig) + } + } + + if let workingDirectory, !workingDirectory.isEmpty { + workingDirectory.withCString { cWorkingDir in + surfaceConfig.working_directory = cWorkingDir + createSurface() } } else { - surface = ghostty_surface_new(app, &surfaceConfig) + createSurface() } if surface == nil { diff --git a/Sources/TabManager.swift b/Sources/TabManager.swift index f31afccf..0f47437f 100644 --- a/Sources/TabManager.swift +++ b/Sources/TabManager.swift @@ -16,11 +16,20 @@ class Tab: Identifiable, ObservableObject { @Published var surfaceDirectories: [UUID: String] = [:] var splitViewSize: CGSize = .zero - init(title: String = "Terminal") { + init(title: String = "Terminal", workingDirectory: String? = nil) { self.id = UUID() self.title = title - self.currentDirectory = FileManager.default.homeDirectoryForCurrentUser.path - let surface = TerminalSurface(tabId: id, context: GHOSTTY_SURFACE_CONTEXT_TAB, configTemplate: nil) + let trimmedWorkingDirectory = workingDirectory?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + let hasWorkingDirectory = !trimmedWorkingDirectory.isEmpty + self.currentDirectory = hasWorkingDirectory + ? trimmedWorkingDirectory + : FileManager.default.homeDirectoryForCurrentUser.path + let surface = TerminalSurface( + tabId: id, + context: GHOSTTY_SURFACE_CONTEXT_TAB, + configTemplate: nil, + workingDirectory: hasWorkingDirectory ? trimmedWorkingDirectory : nil + ) self.splitTree = SplitTree(view: surface) self.focusedSurfaceId = surface.id } @@ -302,8 +311,14 @@ class TabManager: ObservableObject { @discardableResult func addTab() -> Tab { - let newTab = Tab(title: "Terminal \(tabs.count + 1)") - tabs.append(newTab) + let workingDirectory = preferredWorkingDirectoryForNewTab() + let newTab = Tab(title: "Terminal \(tabs.count + 1)", workingDirectory: workingDirectory) + let insertIndex = newTabInsertIndex() + if insertIndex >= 0 && insertIndex <= tabs.count { + tabs.insert(newTab, at: insertIndex) + } else { + tabs.append(newTab) + } selectedTabId = newTab.id NotificationCenter.default.post( name: .ghosttyDidFocusTab, @@ -313,6 +328,27 @@ class TabManager: ObservableObject { return newTab } + private func newTabInsertIndex() -> Int { + guard let selectedTabId, + let index = tabs.firstIndex(where: { $0.id == selectedTabId }) else { + return tabs.count + } + return min(index + 1, tabs.count) + } + + private func preferredWorkingDirectoryForNewTab() -> String? { + guard let selectedTabId, + let tab = tabs.first(where: { $0.id == selectedTabId }) else { + return nil + } + let focusedDirectory = tab.focusedSurfaceId + .flatMap { tab.surfaceDirectories[$0] } + let candidate = focusedDirectory ?? tab.currentDirectory + let normalized = normalizeDirectory(candidate) + let trimmed = normalized.trimmingCharacters(in: .whitespacesAndNewlines) + return trimmed.isEmpty ? nil : normalized + } + func moveTabToTop(_ tabId: UUID) { guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return } guard index != 0 else { return }