Revert "Merge pull request #239 from manaflow-ai/issue-151-ssh-remote-port-proxying"
This reverts commit78e4bd32ba, reversing changes made tocf75da8f8a.
This commit is contained in:
parent
78e4bd32ba
commit
f7cbbad434
60 changed files with 1250 additions and 17140 deletions
|
|
@ -30,20 +30,11 @@ enum NewWorkspacePlacement: String, CaseIterable, Identifiable {
|
|||
var description: String {
|
||||
switch self {
|
||||
case .top:
|
||||
return String(
|
||||
localized: "workspace.placement.top.description",
|
||||
defaultValue: "Insert new workspaces at the top of the list."
|
||||
)
|
||||
return String(localized: "workspace.placement.top.description", defaultValue: "Insert new workspaces at the top of the list.")
|
||||
case .afterCurrent:
|
||||
return String(
|
||||
localized: "workspace.placement.afterCurrent.description",
|
||||
defaultValue: "Insert new workspaces directly after the active workspace."
|
||||
)
|
||||
return String(localized: "workspace.placement.afterCurrent.description", defaultValue: "Insert new workspaces directly after the active workspace.")
|
||||
case .end:
|
||||
return String(
|
||||
localized: "workspace.placement.end.description",
|
||||
defaultValue: "Append new workspaces to the bottom of the list."
|
||||
)
|
||||
return String(localized: "workspace.placement.end.description", defaultValue: "Append new workspaces to the bottom of the list.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,9 +72,9 @@ enum SidebarActiveTabIndicatorStyle: String, CaseIterable, Identifiable {
|
|||
var displayName: String {
|
||||
switch self {
|
||||
case .leftRail:
|
||||
return "Left Rail"
|
||||
return String(localized: "sidebar.indicator.leftRail", defaultValue: "Left Rail")
|
||||
case .solidFill:
|
||||
return "Solid Fill"
|
||||
return String(localized: "sidebar.indicator.solidFill", defaultValue: "Solid Fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -741,25 +732,36 @@ class TabManager: ObservableObject {
|
|||
}
|
||||
|
||||
var isFindVisible: Bool {
|
||||
selectedTerminalPanel?.searchState != nil || focusedBrowserPanel?.searchState != nil
|
||||
if selectedTerminalPanel?.searchState != nil { return true }
|
||||
if focusedBrowserPanel?.searchState != nil { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
var canUseSelectionForFind: Bool {
|
||||
selectedTerminalPanel?.hasSelection() == true
|
||||
if focusedBrowserPanel != nil { return false }
|
||||
return selectedTerminalPanel?.hasSelection() == true
|
||||
}
|
||||
|
||||
func startSearch() {
|
||||
if let panel = selectedTerminalPanel {
|
||||
if panel.searchState == nil {
|
||||
panel.searchState = TerminalSurface.SearchState()
|
||||
}
|
||||
NSLog("Find: startSearch workspace=%@ panel=%@", panel.workspaceId.uuidString, panel.id.uuidString)
|
||||
NotificationCenter.default.post(name: .ghosttySearchFocus, object: panel.surface)
|
||||
_ = panel.performBindingAction("start_search")
|
||||
if let browser = focusedBrowserPanel {
|
||||
browser.startFind()
|
||||
return
|
||||
}
|
||||
|
||||
focusedBrowserPanel?.startFind()
|
||||
guard let panel = selectedTerminalPanel else {
|
||||
#if DEBUG
|
||||
dlog("find.startSearch SKIPPED no selectedTerminalPanel")
|
||||
#endif
|
||||
return
|
||||
}
|
||||
let wasNil = panel.searchState == nil
|
||||
if wasNil {
|
||||
panel.searchState = TerminalSurface.SearchState()
|
||||
}
|
||||
#if DEBUG
|
||||
dlog("find.startSearch workspace=\(panel.workspaceId.uuidString.prefix(5)) panel=\(panel.id.uuidString.prefix(5)) created=\(wasNil ? "yes" : "no(reuse)") firstResponder=\(String(describing: panel.surface.hostedView.window?.firstResponder))")
|
||||
#endif
|
||||
NotificationCenter.default.post(name: .ghosttySearchFocus, object: panel.surface)
|
||||
_ = panel.performBindingAction("start_search")
|
||||
}
|
||||
|
||||
func searchSelection() {
|
||||
|
|
@ -767,27 +769,27 @@ class TabManager: ObservableObject {
|
|||
if panel.searchState == nil {
|
||||
panel.searchState = TerminalSurface.SearchState()
|
||||
}
|
||||
NSLog("Find: searchSelection workspace=%@ panel=%@", panel.workspaceId.uuidString, panel.id.uuidString)
|
||||
#if DEBUG
|
||||
dlog("find.searchSelection workspace=\(panel.workspaceId.uuidString.prefix(5)) panel=\(panel.id.uuidString.prefix(5))")
|
||||
#endif
|
||||
NotificationCenter.default.post(name: .ghosttySearchFocus, object: panel.surface)
|
||||
_ = panel.performBindingAction("search_selection")
|
||||
}
|
||||
|
||||
func findNext() {
|
||||
if let panel = selectedTerminalPanel {
|
||||
_ = panel.performBindingAction("search:next")
|
||||
if let browser = focusedBrowserPanel, browser.searchState != nil {
|
||||
browser.findNext()
|
||||
return
|
||||
}
|
||||
|
||||
focusedBrowserPanel?.findNext()
|
||||
_ = selectedTerminalPanel?.performBindingAction("search:next")
|
||||
}
|
||||
|
||||
func findPrevious() {
|
||||
if let panel = selectedTerminalPanel {
|
||||
_ = panel.performBindingAction("search:previous")
|
||||
if let browser = focusedBrowserPanel, browser.searchState != nil {
|
||||
browser.findPrevious()
|
||||
return
|
||||
}
|
||||
|
||||
focusedBrowserPanel?.findPrevious()
|
||||
_ = selectedTerminalPanel?.performBindingAction("search:previous")
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
|
|
@ -797,26 +799,27 @@ class TabManager: ObservableObject {
|
|||
}
|
||||
|
||||
func hideFind() {
|
||||
if let panel = selectedTerminalPanel {
|
||||
panel.searchState = nil
|
||||
if let browser = focusedBrowserPanel, browser.searchState != nil {
|
||||
browser.hideFind()
|
||||
return
|
||||
}
|
||||
|
||||
focusedBrowserPanel?.hideFind()
|
||||
#if DEBUG
|
||||
dlog("find.hideFind panel=\(selectedTerminalPanel?.id.uuidString.prefix(5) ?? "nil")")
|
||||
#endif
|
||||
selectedTerminalPanel?.searchState = nil
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func addWorkspace(
|
||||
workingDirectory overrideWorkingDirectory: String? = nil,
|
||||
initialTerminalCommand: String? = nil,
|
||||
initialTerminalEnvironment: [String: String] = [:],
|
||||
select: Bool = true,
|
||||
eagerLoadTerminal: Bool = false,
|
||||
placementOverride: NewWorkspacePlacement? = nil,
|
||||
autoWelcomeIfNeeded: Bool = true
|
||||
) -> Workspace {
|
||||
sentryBreadcrumb("workspace.create", data: ["tabCount": tabs.count + 1])
|
||||
let workingDirectory = normalizedWorkingDirectory(overrideWorkingDirectory) ?? preferredWorkingDirectoryForNewTab()
|
||||
let explicitWorkingDirectory = normalizedWorkingDirectory(overrideWorkingDirectory)
|
||||
let workingDirectory = explicitWorkingDirectory ?? preferredWorkingDirectoryForNewTab()
|
||||
let inheritedConfig = inheritedTerminalConfigForNewWorkspace()
|
||||
let ordinal = Self.nextPortOrdinal
|
||||
Self.nextPortOrdinal += 1
|
||||
|
|
@ -824,9 +827,7 @@ class TabManager: ObservableObject {
|
|||
title: "Terminal \(tabs.count + 1)",
|
||||
workingDirectory: workingDirectory,
|
||||
portOrdinal: ordinal,
|
||||
configTemplate: inheritedConfig,
|
||||
initialTerminalCommand: initialTerminalCommand,
|
||||
initialTerminalEnvironment: initialTerminalEnvironment
|
||||
configTemplate: inheritedConfig
|
||||
)
|
||||
wireClosedBrowserTracking(for: newWorkspace)
|
||||
let insertIndex = newTabInsertIndex(placementOverride: placementOverride)
|
||||
|
|
@ -835,8 +836,17 @@ class TabManager: ObservableObject {
|
|||
} else {
|
||||
tabs.append(newWorkspace)
|
||||
}
|
||||
if let explicitWorkingDirectory,
|
||||
let terminalPanel = newWorkspace.focusedTerminalPanel {
|
||||
scheduleInitialWorkspaceGitMetadataRefresh(
|
||||
workspaceId: newWorkspace.id,
|
||||
panelId: terminalPanel.id,
|
||||
directory: explicitWorkingDirectory
|
||||
)
|
||||
}
|
||||
if eagerLoadTerminal {
|
||||
newWorkspace.focusedTerminalPanel?.surface.requestBackgroundSurfaceStartIfNeeded()
|
||||
requestBackgroundWorkspaceLoad(for: newWorkspace.id)
|
||||
newWorkspace.requestBackgroundTerminalSurfaceStartIfNeeded()
|
||||
}
|
||||
if select {
|
||||
selectedTabId = newWorkspace.id
|
||||
|
|
@ -1152,6 +1162,16 @@ class TabManager: ObservableObject {
|
|||
tabs.insert(tab, at: insertIndex)
|
||||
}
|
||||
|
||||
func moveTabToTopForNotification(_ tabId: UUID) {
|
||||
guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return }
|
||||
let pinnedCount = tabs.filter { $0.isPinned }.count
|
||||
guard index != pinnedCount else { return }
|
||||
let tab = tabs[index]
|
||||
guard !tab.isPinned else { return }
|
||||
tabs.remove(at: index)
|
||||
tabs.insert(tab, at: pinnedCount)
|
||||
}
|
||||
|
||||
func moveTabsToTop(_ tabIds: Set<UUID>) {
|
||||
guard !tabIds.isEmpty else { return }
|
||||
let selectedTabs = tabs.filter { tabIds.contains($0.id) }
|
||||
|
|
@ -1164,16 +1184,6 @@ class TabManager: ObservableObject {
|
|||
tabs = selectedPinned + remainingPinned + selectedUnpinned + remainingUnpinned
|
||||
}
|
||||
|
||||
func moveTabToTopForNotification(_ tabId: UUID) {
|
||||
guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return }
|
||||
let pinnedCount = tabs.filter { $0.isPinned }.count
|
||||
guard index != pinnedCount else { return }
|
||||
let tab = tabs[index]
|
||||
guard !tab.isPinned else { return }
|
||||
tabs.remove(at: index)
|
||||
tabs.insert(tab, at: pinnedCount)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func reorderWorkspace(tabId: UUID, toIndex targetIndex: Int) -> Bool {
|
||||
guard let currentIndex = tabs.firstIndex(where: { $0.id == tabId }) else { return false }
|
||||
|
|
@ -1259,23 +1269,22 @@ class TabManager: ObservableObject {
|
|||
|
||||
func closeWorkspace(_ workspace: Workspace) {
|
||||
guard tabs.count > 1 else { return }
|
||||
guard let index = tabs.firstIndex(where: { $0.id == workspace.id }) else { return }
|
||||
sentryBreadcrumb("workspace.close", data: ["tabCount": tabs.count - 1])
|
||||
clearInitialWorkspaceGitProbe(workspaceId: workspace.id)
|
||||
|
||||
AppDelegate.shared?.notificationStore?.clearNotifications(forTabId: workspace.id)
|
||||
workspace.teardownAllPanels()
|
||||
workspace.teardownRemoteConnection()
|
||||
unwireClosedBrowserTracking(for: workspace)
|
||||
workspace.teardownAllPanels()
|
||||
|
||||
if let index = tabs.firstIndex(where: { $0.id == workspace.id }) {
|
||||
tabs.remove(at: index)
|
||||
tabs.remove(at: index)
|
||||
|
||||
if selectedTabId == workspace.id {
|
||||
// Keep the "focused index" stable when possible:
|
||||
// - If we closed workspace i and there is still a workspace at index i, focus it (the one that moved up).
|
||||
// - Otherwise (we closed the last workspace), focus the new last workspace (i-1).
|
||||
let newIndex = min(index, max(0, tabs.count - 1))
|
||||
selectedTabId = tabs[newIndex].id
|
||||
}
|
||||
if selectedTabId == workspace.id {
|
||||
// Keep the "focused index" stable when possible:
|
||||
// - If we closed workspace i and there is still a workspace at index i, focus it (the one that moved up).
|
||||
// - Otherwise (we closed the last workspace), focus the new last workspace (i-1).
|
||||
let newIndex = min(index, max(0, tabs.count - 1))
|
||||
selectedTabId = tabs[newIndex].id
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1284,6 +1293,7 @@ class TabManager: ObservableObject {
|
|||
@discardableResult
|
||||
func detachWorkspace(tabId: UUID) -> Workspace? {
|
||||
guard let index = tabs.firstIndex(where: { $0.id == tabId }) else { return nil }
|
||||
clearInitialWorkspaceGitProbe(workspaceId: tabId)
|
||||
|
||||
let removed = tabs.remove(at: index)
|
||||
unwireClosedBrowserTracking(for: removed)
|
||||
|
|
@ -1345,9 +1355,13 @@ class TabManager: ObservableObject {
|
|||
|
||||
let count = plan.panelIds.count
|
||||
let titleLines = plan.titles.map { "• \($0)" }.joined(separator: "\n")
|
||||
let message = "This is about to close \(count) tab\(count == 1 ? "" : "s") in this pane:\n\(titleLines)"
|
||||
let message = if count == 1 {
|
||||
String(localized: "dialog.closeOtherTabs.message.one", defaultValue: "This will close 1 tab in this pane:\n\(titleLines)")
|
||||
} else {
|
||||
String(localized: "dialog.closeOtherTabs.message.other", defaultValue: "This will close \(count) tabs in this pane:\n\(titleLines)")
|
||||
}
|
||||
guard confirmClose(
|
||||
title: "Close other tabs?",
|
||||
title: String(localized: "dialog.closeOtherTabs.title", defaultValue: "Close other tabs?"),
|
||||
message: message,
|
||||
acceptCmdD: false
|
||||
) else { return }
|
||||
|
|
@ -1387,8 +1401,8 @@ class TabManager: ObservableObject {
|
|||
alert.messageText = title
|
||||
alert.informativeText = message
|
||||
alert.alertStyle = .warning
|
||||
alert.addButton(withTitle: "Close")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.addButton(withTitle: String(localized: "common.close", defaultValue: "Close"))
|
||||
alert.addButton(withTitle: String(localized: "common.cancel", defaultValue: "Cancel"))
|
||||
|
||||
// macOS convention: Cmd+D = confirm destructive close (e.g. "Don't Save").
|
||||
// We only opt into this for the "close last workspace => close window" path to avoid
|
||||
|
|
@ -1449,15 +1463,15 @@ class TabManager: ObservableObject {
|
|||
if let collapsed, !collapsed.isEmpty {
|
||||
return collapsed
|
||||
}
|
||||
return "Untitled Tab"
|
||||
return String(localized: "tab.untitled", defaultValue: "Untitled Tab")
|
||||
}
|
||||
|
||||
private func closeWorkspaceIfRunningProcess(_ workspace: Workspace) {
|
||||
let willCloseWindow = tabs.count <= 1
|
||||
if workspaceNeedsConfirmClose(workspace),
|
||||
!confirmClose(
|
||||
title: "Close workspace?",
|
||||
message: "This will close the workspace and all of its panels.",
|
||||
title: String(localized: "dialog.closeWorkspace.title", defaultValue: "Close workspace?"),
|
||||
message: String(localized: "dialog.closeWorkspace.message", defaultValue: "This will close the workspace and all of its panels."),
|
||||
acceptCmdD: willCloseWindow
|
||||
) {
|
||||
return
|
||||
|
|
@ -1498,8 +1512,8 @@ class TabManager: ObservableObject {
|
|||
let needsConfirm = workspaceNeedsConfirmClose(tab)
|
||||
if needsConfirm {
|
||||
let message = willCloseWindow
|
||||
? "This will close the last tab and close the window."
|
||||
: "This will close the last tab and close its workspace."
|
||||
? String(localized: "dialog.closeLastTabWindow.message", defaultValue: "This will close the last tab and close the window.")
|
||||
: String(localized: "dialog.closeLastTabWorkspace.message", defaultValue: "This will close the last tab and close its workspace.")
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"surface.close.shortcut.confirm tab=\(tab.id.uuidString.prefix(5)) " +
|
||||
|
|
@ -1507,7 +1521,7 @@ class TabManager: ObservableObject {
|
|||
)
|
||||
#endif
|
||||
guard confirmClose(
|
||||
title: "Close tab?",
|
||||
title: String(localized: "dialog.closeTab.title", defaultValue: "Close tab?"),
|
||||
message: message,
|
||||
acceptCmdD: willCloseWindow
|
||||
) else {
|
||||
|
|
@ -1539,8 +1553,8 @@ class TabManager: ObservableObject {
|
|||
)
|
||||
#endif
|
||||
guard confirmClose(
|
||||
title: "Close tab?",
|
||||
message: "This will close the current tab.",
|
||||
title: String(localized: "dialog.closeTab.title", defaultValue: "Close tab?"),
|
||||
message: String(localized: "dialog.closeTab.message", defaultValue: "This will close the current tab."),
|
||||
acceptCmdD: false
|
||||
) else {
|
||||
#if DEBUG
|
||||
|
|
@ -1578,8 +1592,8 @@ class TabManager: ObservableObject {
|
|||
if let terminalPanel = tab.terminalPanel(for: surfaceId),
|
||||
terminalPanel.needsConfirmClose() {
|
||||
guard confirmClose(
|
||||
title: "Close tab?",
|
||||
message: "This will close the current tab.",
|
||||
title: String(localized: "dialog.closeTab.title", defaultValue: "Close tab?"),
|
||||
message: String(localized: "dialog.closeTab.message", defaultValue: "This will close the current tab."),
|
||||
acceptCmdD: false
|
||||
) else { return }
|
||||
}
|
||||
|
|
@ -1846,28 +1860,32 @@ class TabManager: ObservableObject {
|
|||
guard !shouldSuppressFlash else { return }
|
||||
guard AppFocusState.isAppActive() else { return }
|
||||
guard let panelId = focusedPanelId(for: tabId) else { return }
|
||||
markPanelReadOnFocusIfActive(tabId: tabId, panelId: panelId)
|
||||
_ = dismissNotificationIfActive(tabId: tabId, surfaceId: panelId, triggerFlash: true)
|
||||
}
|
||||
|
||||
private func markPanelReadOnFocusIfActive(tabId: UUID, panelId: UUID) {
|
||||
guard selectedTabId == tabId else { return }
|
||||
guard !suppressFocusFlash else { return }
|
||||
guard AppFocusState.isAppActive() else { return }
|
||||
guard let notificationStore = AppDelegate.shared?.notificationStore else { return }
|
||||
guard notificationStore.hasUnreadNotification(forTabId: tabId, surfaceId: panelId) else { return }
|
||||
if let tab = tabs.first(where: { $0.id == tabId }) {
|
||||
tab.triggerNotificationFocusFlash(panelId: panelId, requiresSplit: false, shouldFocus: false)
|
||||
}
|
||||
notificationStore.markRead(forTabId: tabId, surfaceId: panelId)
|
||||
_ = dismissNotificationIfActive(tabId: tabId, surfaceId: panelId, triggerFlash: true)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func dismissNotificationOnDirectInteraction(tabId: UUID, surfaceId: UUID?) -> Bool {
|
||||
dismissNotificationIfActive(tabId: tabId, surfaceId: surfaceId, triggerFlash: true)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private func dismissNotificationIfActive(
|
||||
tabId: UUID,
|
||||
surfaceId: UUID?,
|
||||
triggerFlash: Bool
|
||||
) -> Bool {
|
||||
guard selectedTabId == tabId else { return false }
|
||||
guard AppFocusState.isAppActive() else { return false }
|
||||
guard let notificationStore = AppDelegate.shared?.notificationStore else { return false }
|
||||
guard notificationStore.hasUnreadNotification(forTabId: tabId, surfaceId: surfaceId) else { return false }
|
||||
if let panelId = surfaceId,
|
||||
if triggerFlash,
|
||||
let panelId = surfaceId,
|
||||
let tab = tabs.first(where: { $0.id == tabId }) {
|
||||
tab.triggerNotificationFocusFlash(panelId: panelId, requiresSplit: false, shouldFocus: false)
|
||||
}
|
||||
|
|
@ -2166,9 +2184,24 @@ class TabManager: ObservableObject {
|
|||
guard let selectedTabId,
|
||||
let tab = tabs.first(where: { $0.id == selectedTabId }),
|
||||
let focusedPanelId = tab.focusedPanelId else { return }
|
||||
#if DEBUG
|
||||
let directionLabel = direction.debugLabel
|
||||
dlog(
|
||||
"split.create.request kind=terminal dir=\(directionLabel) " +
|
||||
"tab=\(selectedTabId.uuidString.prefix(5)) panel=\(focusedPanelId.uuidString.prefix(5)) " +
|
||||
"panels=\(tab.panels.count) panes=\(tab.bonsplitController.allPaneIds.count)"
|
||||
)
|
||||
#endif
|
||||
tab.clearSplitZoom()
|
||||
sentryBreadcrumb("split.create", data: ["direction": String(describing: direction)])
|
||||
_ = newSplit(tabId: selectedTabId, surfaceId: focusedPanelId, direction: direction)
|
||||
let createdPanelId = newSplit(tabId: selectedTabId, surfaceId: focusedPanelId, direction: direction)
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"split.create.result kind=terminal dir=\(directionLabel) " +
|
||||
"created=\(createdPanelId?.uuidString.prefix(5) ?? "nil") " +
|
||||
"panels=\(tab.panels.count) panes=\(tab.bonsplitController.allPaneIds.count)"
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Create a new browser split from the currently focused panel.
|
||||
|
|
@ -2177,14 +2210,30 @@ class TabManager: ObservableObject {
|
|||
guard let selectedTabId,
|
||||
let tab = tabs.first(where: { $0.id == selectedTabId }),
|
||||
let focusedPanelId = tab.focusedPanelId else { return nil }
|
||||
#if DEBUG
|
||||
let directionLabel = direction.debugLabel
|
||||
dlog(
|
||||
"split.create.request kind=browser dir=\(directionLabel) " +
|
||||
"tab=\(selectedTabId.uuidString.prefix(5)) panel=\(focusedPanelId.uuidString.prefix(5)) " +
|
||||
"panels=\(tab.panels.count) panes=\(tab.bonsplitController.allPaneIds.count)"
|
||||
)
|
||||
#endif
|
||||
tab.clearSplitZoom()
|
||||
return newBrowserSplit(
|
||||
let createdPanelId = newBrowserSplit(
|
||||
tabId: selectedTabId,
|
||||
fromPanelId: focusedPanelId,
|
||||
orientation: direction.orientation,
|
||||
insertFirst: direction.insertFirst,
|
||||
url: url
|
||||
)
|
||||
#if DEBUG
|
||||
dlog(
|
||||
"split.create.result kind=browser dir=\(directionLabel) " +
|
||||
"created=\(createdPanelId?.uuidString.prefix(5) ?? "nil") " +
|
||||
"panels=\(tab.panels.count) panes=\(tab.bonsplitController.allPaneIds.count)"
|
||||
)
|
||||
#endif
|
||||
return createdPanelId
|
||||
}
|
||||
|
||||
/// Refresh Bonsplit right-side action button tooltips for all workspaces.
|
||||
|
|
@ -2285,12 +2334,21 @@ class TabManager: ObservableObject {
|
|||
/// Returns the new panel's ID (which is also the surface ID for terminals)
|
||||
func newSplit(tabId: UUID, surfaceId: UUID, direction: SplitDirection, focus: Bool = true) -> UUID? {
|
||||
guard let tab = tabs.first(where: { $0.id == tabId }) else { return nil }
|
||||
return tab.newTerminalSplit(
|
||||
let createdPanel = tab.newTerminalSplit(
|
||||
from: surfaceId,
|
||||
orientation: direction.orientation,
|
||||
insertFirst: direction.insertFirst,
|
||||
focus: focus
|
||||
)?.id
|
||||
#if DEBUG
|
||||
let directionLabel = direction.debugLabel
|
||||
dlog(
|
||||
"split.newSurface result dir=\(directionLabel) " +
|
||||
"tab=\(tabId.uuidString.prefix(5)) source=\(surfaceId.uuidString.prefix(5)) " +
|
||||
"created=\(createdPanel?.uuidString.prefix(5) ?? "nil") focus=\(focus ? 1 : 0)"
|
||||
)
|
||||
#endif
|
||||
return createdPanel
|
||||
}
|
||||
|
||||
/// Move focus in the specified direction
|
||||
|
|
@ -2891,7 +2949,7 @@ class TabManager: ObservableObject {
|
|||
continue
|
||||
}
|
||||
terminal.hostedView.reconcileGeometryNow()
|
||||
terminal.surface.forceRefresh()
|
||||
terminal.surface.forceRefresh(reason: "tabManager.reconcileVisibleTerminalGeometry")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3869,6 +3927,15 @@ enum SplitDirection {
|
|||
var insertFirst: Bool {
|
||||
self == .left || self == .up
|
||||
}
|
||||
|
||||
var debugLabel: String {
|
||||
switch self {
|
||||
case .left: return "left"
|
||||
case .right: return "right"
|
||||
case .up: return "up"
|
||||
case .down: return "down"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resize direction for backwards compatibility
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue