fix: sync SFTP directory context
This commit is contained in:
1 parent
264a2dc8ae
commit
339665e6fb
3 files changed
+64
-2
No files matched your search
@@ -1,5 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
protocol SFTPSessionNavigationRouting: AnyObject {
|
||||
func sftpSession(_ sftpSession: SFTPSessionModel, didChangePath path: String)
|
||||
}
|
||||
|
||||
/// State consumed by SwiftUI's macOS `Table` must change as one value.
|
||||
///
|
||||
/// `Table` is backed by `NSOutlineView`. Publishing its rows, filter, and
|
||||
@@ -64,7 +68,12 @@ final class SFTPSessionModel: Identifiable {
|
||||
let id = UUID()
|
||||
let hostID: Host.ID
|
||||
let initialPath: String
|
||||
private(set) var path: String
|
||||
private(set) var path: String {
|
||||
didSet {
|
||||
guard path != oldValue else { return }
|
||||
navigationRouter?.sftpSession(self, didChangePath: path)
|
||||
}
|
||||
}
|
||||
var pathText: String
|
||||
private(set) var backHistory: [String] = []
|
||||
private(set) var forwardHistory: [String] = []
|
||||
@@ -117,6 +126,7 @@ final class SFTPSessionModel: Identifiable {
|
||||
private var hasConnectedOnce = false
|
||||
private var isRecoveringConnection = false
|
||||
private var operationCancellation: TransferCancellationToken?
|
||||
weak var navigationRouter: (any SFTPSessionNavigationRouting)?
|
||||
private weak var statusStore: HostStore?
|
||||
private weak var transferCoordinator: SFTPTransferCoordinator?
|
||||
private var didResolveInitialPath: Bool
|
||||
|
||||
@@ -221,30 +221,46 @@ final class Workspace: Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps the sshSession-to-Workspace relationship correct as panes are merged,
|
||||
/// Keeps each session-to-Workspace relationship correct as panes are merged,
|
||||
/// detached, replaced, and tiled. Removed sessions are cleared only when
|
||||
/// this Workspace still owns their route; a destination may already have
|
||||
/// claimed them during a tree rewrite.
|
||||
private func synchronizeInputRouters(previousLayout: SplitNode? = nil) {
|
||||
let currentSessions = broadcastTargets
|
||||
let currentIDs = Set(currentSessions.map(\.id))
|
||||
let currentSFTPSessions = panes.compactMap(\.sftpSession)
|
||||
let currentSFTPIDs = Set(currentSFTPSessions.map(\.id))
|
||||
|
||||
if let previousLayout {
|
||||
for sshSession in previousLayout.panes.compactMap(\.sshSession)
|
||||
where !currentIDs.contains(sshSession.id) && sshSession.inputRouter === self {
|
||||
sshSession.inputRouter = nil
|
||||
}
|
||||
for sftpSession in previousLayout.panes.compactMap(\.sftpSession)
|
||||
where !currentSFTPIDs.contains(sftpSession.id) && sftpSession.navigationRouter === self {
|
||||
sftpSession.navigationRouter = nil
|
||||
}
|
||||
}
|
||||
|
||||
for sshSession in currentSessions {
|
||||
sshSession.inputRouter = self
|
||||
}
|
||||
for sftpSession in currentSFTPSessions {
|
||||
sftpSession.navigationRouter = self
|
||||
}
|
||||
|
||||
if !canBroadcast { broadcastsInput = false }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Workspace: SFTPSessionNavigationRouting {
|
||||
func sftpSession(_ sftpSession: SFTPSessionModel, didChangePath path: String) {
|
||||
guard focusedPane.sftpSession === sftpSession else { return }
|
||||
lifecycleRouter?.workspaceContextDidChange(self)
|
||||
}
|
||||
}
|
||||
|
||||
extension Workspace: SSHSessionInputRouting {
|
||||
/// Copies the exact bytes Ghostty produced for a keyboard or paste action.
|
||||
/// Sending directly preserves each event's terminal encoding and avoids
|
||||
|
||||
@@ -110,6 +110,42 @@ struct WindowStateTests {
|
||||
#expect(window.sidebar.assistantDraft.contextChips.map(\.label) == ["Files", "/srv/app"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func focusedSFTPPathChangesUpdateAssistantContext() {
|
||||
let host = Host(name: "Files", host: "files.example")
|
||||
let workspace = Workspace.sftpSession(host: host, initialPath: "/srv")
|
||||
let sftpSession = workspace.focusedPane.sftpSession!
|
||||
let window = WindowModel(tabs: [workspace])
|
||||
window.hostStore = HostStore(hosts: [host])
|
||||
|
||||
sftpSession.navigate(to: "/srv/releases")
|
||||
|
||||
#expect(window.sidebar.assistantDraft.contextChips.map(\.kind) == [.host, .directory])
|
||||
#expect(window.sidebar.assistantDraft.contextChips.map(\.label) == ["Files", "/srv/releases"])
|
||||
|
||||
sftpSession.goBack()
|
||||
#expect(window.sidebar.assistantDraft.contextChips.map(\.label) == ["Files", "/srv"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func backgroundSFTPPathDoesNotReplaceFocusedPaneContext() {
|
||||
let host = Host(name: "Files", host: "files.example")
|
||||
let workspace = Workspace.sftpSession(host: host, initialPath: "/focused")
|
||||
let focusedPane = workspace.focusedPane
|
||||
workspace.splitFocusedPane(
|
||||
axis: .horizontal,
|
||||
with: .sftpSession(host: host, initialPath: "/background")
|
||||
)
|
||||
let backgroundSession = workspace.focusedPane.sftpSession!
|
||||
workspace.focus(focusedPane.id)
|
||||
let window = WindowModel(tabs: [workspace])
|
||||
window.hostStore = HostStore(hosts: [host])
|
||||
|
||||
backgroundSession.navigate(to: "/background/changed")
|
||||
|
||||
#expect(window.sidebar.assistantDraft.contextChips.map(\.label) == ["Files", "/focused"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func openingSidebarConversationRefreshesPinnedContextAndPreservesAttachments() {
|
||||
let host = Host(name: "Server", host: "server.example")
|
||||
|
||||
Reference in new issue
Block a user