feat: Quick Open with more transports
This commit is contained in:
1 parent
286045ab24
commit
58686b400b
4 files changed
+212
-20
No files matched your search
@@ -17349,6 +17349,88 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Connect with Eternal Terminal · %@" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Mit Eternal Terminal verbinden · %@"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Se connecter via Eternal Terminal · %@"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Eternal Terminalで接続 · %@"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Eternal Terminal로 연결 · %@"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用Eternal Terminal连接·%@"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用Eternal Terminal連線·%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Connect with Mosh · %@" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Mit Mosh verbinden · %@"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Se connecter via Mosh · %@"
|
||||
}
|
||||
},
|
||||
"ja" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Moshで接続 · %@"
|
||||
}
|
||||
},
|
||||
"ko" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Mosh로 연결 · %@"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用Mosh连接·%@"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "使用Mosh連線·%@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Connect with SSH · %@" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
||||
@@ -134,6 +134,12 @@ extension AppStrings {
|
||||
public static func connectWithSsh(_ value: String) -> LocalizedStringResource {
|
||||
"Connect with SSH · \(value)"
|
||||
}
|
||||
public static func connectWithMosh(_ value: String) -> LocalizedStringResource {
|
||||
"Connect with Mosh · \(value)"
|
||||
}
|
||||
public static func connectWithEternalTerminal(_ value: String) -> LocalizedStringResource {
|
||||
"Connect with Eternal Terminal · \(value)"
|
||||
}
|
||||
public static func connectWithTelnet(_ value: String) -> LocalizedStringResource {
|
||||
"Connect with Telnet · \(value)"
|
||||
}
|
||||
|
||||
@@ -192,18 +192,40 @@ final class AppCommandRegistry {
|
||||
|
||||
private func hostCommands(_ host: Host) -> [AppCommandDescriptor] {
|
||||
let keywords = [host.host] + host.supportedTerminalProtocols.map { host.displayAddress(for: $0) } + host.tags
|
||||
var commands: [AppCommandDescriptor] = []
|
||||
var protocols = host.supportedTerminalProtocols
|
||||
if let preferred = host.preferredTerminalProtocol {
|
||||
protocols.removeAll { $0 == preferred }
|
||||
protocols.insert(preferred, at: 0)
|
||||
}
|
||||
var commands = protocols.map { transport in
|
||||
let address = host.displayAddress(for: transport)
|
||||
let action: HostConnectAction
|
||||
let subtitle: LocalizedStringResource
|
||||
switch transport {
|
||||
case .ssh:
|
||||
action = .ssh
|
||||
subtitle = AppStrings.Connections.connectWithSsh(address)
|
||||
case .mosh:
|
||||
action = .mosh
|
||||
subtitle = AppStrings.Connections.connectWithMosh(address)
|
||||
case .eternalTerminal:
|
||||
action = .eternalTerminal
|
||||
subtitle = AppStrings.Connections.connectWithEternalTerminal(address)
|
||||
case .telnet:
|
||||
action = .telnet
|
||||
subtitle = AppStrings.Connections.connectWithTelnet(address)
|
||||
}
|
||||
return AppCommandDescriptor(
|
||||
id: "host.\(transport.rawValue).\(host.id.uuidString)",
|
||||
title: host.name,
|
||||
subtitle: String(localized: subtitle),
|
||||
symbol: action.symbol,
|
||||
section: .hosts,
|
||||
keywords: keywords + [transport.rawValue, transport.displayName],
|
||||
target: .connectHost(host.id, action.target)
|
||||
)
|
||||
}
|
||||
if host.sshEnabled {
|
||||
commands.append(
|
||||
AppCommandDescriptor(
|
||||
id: "host.ssh.\(host.id.uuidString)",
|
||||
title: host.name,
|
||||
subtitle: String(localized: AppStrings.Connections.connectWithSsh(host.displayAddress(for: .ssh))),
|
||||
symbol: "apple.terminal",
|
||||
section: .hosts,
|
||||
keywords: keywords + ["ssh"],
|
||||
target: .connectHost(host.id, .sshSession)
|
||||
))
|
||||
commands.append(
|
||||
AppCommandDescriptor(
|
||||
id: "host.sftp.\(host.id.uuidString)",
|
||||
@@ -215,15 +237,6 @@ final class AppCommandRegistry {
|
||||
target: .connectHost(host.id, .sftpChannel)
|
||||
))
|
||||
}
|
||||
if host.telnetEnabled {
|
||||
commands.append(
|
||||
AppCommandDescriptor(
|
||||
id: "host.telnet.\(host.id.uuidString)", title: host.name,
|
||||
subtitle: String(
|
||||
localized: AppStrings.Connections.connectWithTelnet(host.displayAddress(for: .telnet))),
|
||||
symbol: MenuSymbol.telnet, section: .hosts, keywords: keywords + ["telnet"],
|
||||
target: .connectHost(host.id, .telnetSession)))
|
||||
}
|
||||
return commands
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,97 @@ struct QuickOpenTests {
|
||||
#expect(window.workspaces[0].panes.map(\.hostID) == [ssh.id, telnet.id])
|
||||
}
|
||||
|
||||
@Test(arguments: [HostTerminalProtocol.ssh, .mosh, .eternalTerminal])
|
||||
func quickOpenPrioritizesAndOpensTheDefaultTransport(transport: HostTerminalProtocol) throws {
|
||||
var host = Host(name: "Production", host: "server.invalid")
|
||||
host.terminalTransports.moshEnabled = true
|
||||
host.terminalTransports.eternalTerminalEnabled = true
|
||||
host.terminalTransports.defaultTransport = transport
|
||||
let app = AppModel(hosts: HostLibraryModel(hosts: [host]))
|
||||
defer { app.shutdownForTermination() }
|
||||
let window = WindowModel()
|
||||
let results = QuickOpenResults(commands: app.commands.commands(app: app, window: window), query: host.name)
|
||||
let first = try #require(results.commands.first)
|
||||
|
||||
#expect(first.symbol == transport.symbol)
|
||||
app.commands.perform(first.target, app: app, window: window)
|
||||
let session = try #require(window.selectedWorkspace?.focusedPane.sshSession)
|
||||
#expect(session.hostID == host.id)
|
||||
#expect(session.transport == transport)
|
||||
|
||||
for (target, expected) in [
|
||||
(HostOpenTarget.sshSession, HostTerminalProtocol.ssh),
|
||||
(.moshSession, .mosh),
|
||||
(.eternalTerminalSession, .eternalTerminal),
|
||||
] {
|
||||
let command = try #require(results.commands.first { $0.target == .connectHost(host.id, target) })
|
||||
app.commands.perform(command.target, app: app, window: window)
|
||||
#expect(window.selectedWorkspace?.focusedPane.sshSession?.transport == expected)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: [false, true], [false, true])
|
||||
func quickOpenListsOnlyEnabledResumableTransports(moshEnabled: Bool, eternalTerminalEnabled: Bool) throws {
|
||||
var host = Host(name: "Production", host: "server.invalid", port: 2222, tags: ["critical"])
|
||||
host.terminalTransports.moshEnabled = moshEnabled
|
||||
host.terminalTransports.eternalTerminalEnabled = eternalTerminalEnabled
|
||||
host.terminalTransports.eternalTerminalPort = 3022
|
||||
let app = AppModel(hosts: HostLibraryModel(hosts: [host]))
|
||||
defer { app.shutdownForTermination() }
|
||||
let commands = app.commands.commands(app: app, window: WindowModel()).filter { $0.section == .hosts }
|
||||
|
||||
#expect(commands.contains { $0.target == .connectHost(host.id, .moshSession) } == moshEnabled)
|
||||
#expect(
|
||||
commands.contains { $0.target == .connectHost(host.id, .eternalTerminalSession) } == eternalTerminalEnabled)
|
||||
#expect(commands.contains { $0.target == .connectHost(host.id, .sftpChannel) })
|
||||
#expect(Set(commands.map(\.id)).count == commands.count)
|
||||
#expect(commands.allSatisfy { $0.matches("critical") })
|
||||
if moshEnabled {
|
||||
let mosh = try #require(commands.first { $0.target == .connectHost(host.id, .moshSession) })
|
||||
#expect(mosh.symbol == MenuSymbol.mosh)
|
||||
#expect(mosh.matches("Mosh"))
|
||||
#expect(mosh.subtitle == String(localized: AppStrings.Connections.connectWithMosh("server.invalid:2222")))
|
||||
}
|
||||
if eternalTerminalEnabled {
|
||||
let et = try #require(commands.first { $0.target == .connectHost(host.id, .eternalTerminalSession) })
|
||||
#expect(et.symbol == MenuSymbol.eternalTerminal)
|
||||
#expect(et.matches("Eternal Terminal"))
|
||||
#expect(
|
||||
et.subtitle
|
||||
== String(localized: AppStrings.Connections.connectWithEternalTerminal("server.invalid:3022")))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func quickOpenFallsBackWhenTheDefaultTransportIsUnavailable() {
|
||||
func targets(for host: Host) -> [AppCommandTarget] {
|
||||
let app = AppModel(hosts: HostLibraryModel(hosts: [host]))
|
||||
defer { app.shutdownForTermination() }
|
||||
return app.commands.commands(app: app, window: WindowModel())
|
||||
.filter { $0.section == .hosts }.map(\.target)
|
||||
}
|
||||
var host = Host(name: "Production", host: "server.invalid")
|
||||
host.terminalTransports.defaultTransport = .mosh
|
||||
|
||||
#expect(targets(for: host) == [.connectHost(host.id, .sshSession), .connectHost(host.id, .sftpChannel)])
|
||||
|
||||
host.terminalTransports.moshEnabled = true
|
||||
host.terminalTransports.eternalTerminalEnabled = true
|
||||
host.tailscale = .init(groupID: UUID(), stableNodeID: "quick-open-test")
|
||||
#expect(
|
||||
targets(for: host) == [
|
||||
.connectHost(host.id, .sshSession), .connectHost(host.id, .eternalTerminalSession),
|
||||
.connectHost(host.id, .sftpChannel),
|
||||
])
|
||||
|
||||
host.sshEnabled = false
|
||||
host.telnetEnabled = true
|
||||
#expect(targets(for: host) == [.connectHost(host.id, .telnetSession)])
|
||||
|
||||
host.telnetEnabled = false
|
||||
#expect(targets(for: host).isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
func registryCombinesManagementWorkspacesHostsAndActions() {
|
||||
let host = Host(name: "Production", host: "prod.example", tags: ["critical"])
|
||||
|
||||
Reference in new issue
Block a user