Change Telnet's icon
This commit is contained in:
1 parent
0f6c365dd5
commit
90fb1bfbcc
13 files changed
+94
-33
No files matched your search
@@ -21,7 +21,7 @@ public nonisolated struct ConnectionHistoryEntry: Identifiable, Hashable, Sendab
|
||||
public var symbol: String {
|
||||
switch self {
|
||||
case .ssh: "terminal"
|
||||
case .telnet: "terminal"
|
||||
case .telnet: "text.rectangle"
|
||||
case .sftp: "folder"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,14 +233,7 @@ private struct JourneyEndpointView: View {
|
||||
#endif
|
||||
case .jump: return "server.rack"
|
||||
case .destination:
|
||||
return switch purpose {
|
||||
case .sshSession: "apple.terminal.fill"
|
||||
case .telnetSession: "terminal.fill"
|
||||
case .sftpChannel: "folder.fill"
|
||||
case .metrics: "waveform.path.ecg"
|
||||
case .portForwarding: "arrow.left.arrow.right"
|
||||
case .serialSession: "cable.connector"
|
||||
}
|
||||
return purpose.symbol(for: .routeDestination)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
import SwiftUI
|
||||
import TermindApplication
|
||||
|
||||
nonisolated enum ConnectionJourneySymbolPlacement {
|
||||
case header
|
||||
case routeDestination
|
||||
}
|
||||
|
||||
extension ConnectionPurpose {
|
||||
nonisolated func symbol(for placement: ConnectionJourneySymbolPlacement) -> String {
|
||||
switch (self, placement) {
|
||||
case (.sshSession, _): "apple.terminal.fill"
|
||||
case (.telnetSession, _): "text.rectangle.fill"
|
||||
case (.sftpChannel, .header): "folder.fill.badge.gearshape"
|
||||
case (.sftpChannel, .routeDestination): "folder.fill"
|
||||
case (.serialSession, _): "cable.connector"
|
||||
case (.metrics, _): "waveform.path.ecg"
|
||||
case (.portForwarding, _): "arrow.left.arrow.right"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ConnectionJourneyHeader: View {
|
||||
let journey: ConnectionJourney
|
||||
let selectedForeground: Color
|
||||
@@ -29,7 +48,7 @@ public struct ConnectionJourneyHeader: View {
|
||||
.fill(Color.accentColor.gradient)
|
||||
.frame(width: 52, height: 52)
|
||||
.overlay {
|
||||
Image(systemName: symbol)
|
||||
Image(systemName: journey.purpose.symbol(for: .header))
|
||||
.font(.system(size: 22, weight: .semibold))
|
||||
.foregroundStyle(selectedForeground)
|
||||
}
|
||||
@@ -82,16 +101,6 @@ public struct ConnectionJourneyHeader: View {
|
||||
return "\(protocolName) \(address)"
|
||||
}
|
||||
|
||||
private var symbol: String {
|
||||
switch journey.purpose {
|
||||
case .sshSession: "apple.terminal.fill"
|
||||
case .telnetSession: "terminal.fill"
|
||||
case .sftpChannel: "folder.fill.badge.gearshape"
|
||||
case .serialSession: "cable.connector"
|
||||
case .metrics: "waveform.path.ecg"
|
||||
case .portForwarding: "arrow.left.arrow.right"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ConnectionJourneyStatus: View {
|
||||
|
||||
@@ -609,13 +609,13 @@ struct MobileVaultView: View {
|
||||
private func hostMenuButton(_ action: MobileHostMenuAction, host: Host) -> some View {
|
||||
switch action {
|
||||
case .ssh:
|
||||
Button("SSH", systemImage: "terminal") { open(host.id, .ssh) }
|
||||
Button("SSH", systemImage: ConnectionHistoryEntry.Kind.ssh.symbol) { open(host.id, .ssh) }
|
||||
.disabled(!host.sshEnabled)
|
||||
case .telnet:
|
||||
Button("Telnet", systemImage: "terminal") { open(host.id, .telnet) }
|
||||
Button("Telnet", systemImage: ConnectionHistoryEntry.Kind.telnet.symbol) { open(host.id, .telnet) }
|
||||
.disabled(!host.telnetEnabled)
|
||||
case .sftp:
|
||||
Button("SFTP", systemImage: "folder") { open(host.id, .sftp) }
|
||||
Button("SFTP", systemImage: ConnectionHistoryEntry.Kind.sftp.symbol) { open(host.id, .sftp) }
|
||||
.disabled(!host.sshEnabled)
|
||||
case .edit:
|
||||
Button("Edit", systemImage: "pencil") { editingHost = host }
|
||||
|
||||
@@ -11,7 +11,9 @@ import struct TermindDomain.Host
|
||||
struct MobileHomeScreenQuickActionTests {
|
||||
@Test func descriptorsUseTheNewestThreeUniqueValidHistoryRows() throws {
|
||||
let app = try MobileAppModel(inMemory: true)
|
||||
let repeated = Host(name: "Production", host: "production.example.invalid")
|
||||
let repeated = Host(
|
||||
name: "Production", host: "production.example.invalid",
|
||||
telnetEnabled: true)
|
||||
let others = (0..<3).map { Host(name: "Host \($0)", host: "host-\($0).example.invalid") }
|
||||
app.library.upsert(repeated)
|
||||
for host in others { app.library.upsert(host) }
|
||||
@@ -20,6 +22,8 @@ struct MobileHomeScreenQuickActionTests {
|
||||
kind: .ssh, for: repeated.id, at: Date(timeIntervalSince1970: 60))
|
||||
_ = app.connectionHistory.record(
|
||||
kind: .ssh, for: repeated.id, at: Date(timeIntervalSince1970: 50))
|
||||
_ = app.connectionHistory.record(
|
||||
kind: .telnet, for: repeated.id, at: Date(timeIntervalSince1970: 45))
|
||||
_ = app.connectionHistory.record(
|
||||
kind: .sftp, for: repeated.id, at: Date(timeIntervalSince1970: 40))
|
||||
_ = app.connectionHistory.record(
|
||||
@@ -43,11 +47,13 @@ struct MobileHomeScreenQuickActionTests {
|
||||
descriptors.map(\.action)
|
||||
== [
|
||||
.history(hostID: repeated.id, kind: .ssh),
|
||||
.history(hostID: repeated.id, kind: .telnet),
|
||||
.history(hostID: repeated.id, kind: .sftp),
|
||||
.history(hostID: others[0].id, kind: .ssh),
|
||||
])
|
||||
#expect(descriptors.map(\.title) == ["Production", "Production", "Host 0"])
|
||||
#expect(descriptors.map(\.symbol) == ["terminal", "folder", "terminal"])
|
||||
#expect(descriptors.map(\.title) == ["Production", "Production", "Production"])
|
||||
#expect(descriptors.map(\.symbol) == ["terminal", "text.rectangle", "folder"])
|
||||
#expect(UIImage(systemName: descriptors[1].symbol) != nil)
|
||||
#expect(descriptors[1].shortcutItem.icon != nil)
|
||||
}
|
||||
|
||||
@Test func historyShortcutPayloadRoundTripsAndMalformedItemsAreRejected() throws {
|
||||
|
||||
@@ -219,7 +219,7 @@ final class AppCommandRegistry {
|
||||
AppCommandDescriptor(
|
||||
id: "host.telnet.\(host.id.uuidString)", title: host.name,
|
||||
subtitle: String(localized: "Connect with Telnet · \(host.displayAddress(for: .telnet))"),
|
||||
symbol: "terminal", section: .hosts, keywords: keywords + ["telnet"],
|
||||
symbol: MenuSymbol.telnet, section: .hosts, keywords: keywords + ["telnet"],
|
||||
target: .connectHost(host.id, .telnetSession)))
|
||||
}
|
||||
return commands
|
||||
|
||||
@@ -33,6 +33,7 @@ nonisolated enum MenuSymbol {
|
||||
static let connect = "network"
|
||||
static let batchStartup = "wind"
|
||||
static let ssh = "apple.terminal"
|
||||
static let telnet = "text.rectangle"
|
||||
static let sftp = "folder"
|
||||
|
||||
// MARK: Creating
|
||||
@@ -121,7 +122,7 @@ nonisolated enum MenuSymbol {
|
||||
/// is exactly the kind of mistake no one notices in review.
|
||||
static let all: [String] = [
|
||||
settings, quickOpen, find, actions, support, showAssistant,
|
||||
quickConnect, connect, batchStartup, ssh, sftp,
|
||||
quickConnect, connect, batchStartup, ssh, telnet, sftp,
|
||||
newChat, newHost, newGroup, newPackage, newFolder, newSnippet, saveAsSnippet,
|
||||
newIdentity, generateKey, generateCommand,
|
||||
importItem, download, export, upload,
|
||||
|
||||
@@ -190,7 +190,7 @@ enum HostMenuEntries {
|
||||
app.open([host], as: .sshSession, in: window)
|
||||
},
|
||||
NativeMenuEntry(
|
||||
title: String(localized: "with Telnet"), symbol: "terminal",
|
||||
title: String(localized: "with Telnet"), symbol: MenuSymbol.telnet,
|
||||
isEnabled: host.telnetEnabled
|
||||
) {
|
||||
app.open([host], as: .telnetSession, in: window)
|
||||
@@ -231,7 +231,7 @@ struct HostConnectMenu: View {
|
||||
app.open(hosts, as: .sshSession, in: window)
|
||||
}
|
||||
.disabled(!hosts.allSatisfy(\.sshEnabled))
|
||||
Button("with Telnet", systemImage: "terminal") {
|
||||
Button("with Telnet", systemImage: MenuSymbol.telnet) {
|
||||
app.open(hosts, as: .telnetSession, in: window)
|
||||
}
|
||||
.disabled(!hosts.allSatisfy(\.telnetEnabled))
|
||||
|
||||
@@ -162,7 +162,7 @@ final class Pane: Identifiable {
|
||||
var symbol: String {
|
||||
switch content {
|
||||
case .sshSession: "apple.terminal"
|
||||
case .telnetSession: "terminal"
|
||||
case .telnetSession: MenuSymbol.telnet
|
||||
case .sftpChannel: "folder"
|
||||
case .serialSession: "cable.connector"
|
||||
case .empty: "rectangle.dashed"
|
||||
|
||||
@@ -15,6 +15,14 @@ import struct TermindDomain.Host
|
||||
@Suite
|
||||
@MainActor
|
||||
struct ConnectionHistoryTests {
|
||||
@Test
|
||||
func connectionProtocolSymbolsAreDistinct() {
|
||||
#expect(ConnectionHistoryEntry.Kind.ssh.symbol == "terminal")
|
||||
#expect(ConnectionHistoryEntry.Kind.telnet.symbol == "text.rectangle")
|
||||
#expect(ConnectionHistoryEntry.Kind.sftp.symbol == "folder")
|
||||
#expect(Set(ConnectionHistoryEntry.Kind.allCases.map(\.symbol)).count == 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
func schemaV5MigratesWithAnEmptyConnectionTimeline() throws {
|
||||
let directory = FileManager.default.temporaryDirectory
|
||||
|
||||
@@ -3,17 +3,27 @@ import TermindApplication
|
||||
import TermindDomain
|
||||
import TermindInfrastructure
|
||||
import TermindPersistence
|
||||
import TermindPresentation
|
||||
import TermindRemoteInfrastructure
|
||||
import Testing
|
||||
|
||||
import struct TermindDomain.Host
|
||||
|
||||
@testable import Termind
|
||||
@testable import TermindPresentation
|
||||
|
||||
@Suite
|
||||
@MainActor
|
||||
struct ConnectionJourneyTests {
|
||||
@Test
|
||||
func telnetUsesItsOwnSymbolThroughoutTheConnectionJourney() {
|
||||
#expect(ConnectionPurpose.telnetSession.symbol(for: .header) == "text.rectangle.fill")
|
||||
#expect(ConnectionPurpose.telnetSession.symbol(for: .routeDestination) == "text.rectangle.fill")
|
||||
#expect(ConnectionPurpose.sshSession.symbol(for: .header) == "apple.terminal.fill")
|
||||
#expect(ConnectionPurpose.sshSession.symbol(for: .routeDestination) == "apple.terminal.fill")
|
||||
#expect(ConnectionPurpose.sftpChannel.symbol(for: .header) == "folder.fill.badge.gearshape")
|
||||
#expect(ConnectionPurpose.sftpChannel.symbol(for: .routeDestination) == "folder.fill")
|
||||
}
|
||||
|
||||
@Test
|
||||
func routeStartsAtBlueLocalEndpointAndKeepsHostsPending() {
|
||||
let jump = Host(name: "Bastion", host: "jump.example")
|
||||
|
||||
@@ -15,6 +15,29 @@ import struct TermindDomain.Host
|
||||
@Suite
|
||||
@MainActor
|
||||
struct QuickOpenTests {
|
||||
@Test
|
||||
func telnetUsesItsOwnSymbolInQuickOpenAndNativeHostMenus() throws {
|
||||
let host = Host(
|
||||
name: "Legacy Console", host: "console.example.invalid",
|
||||
telnetEnabled: true)
|
||||
let store = HostLibraryModel(hosts: [host])
|
||||
let app = AppModel(hosts: store)
|
||||
let window = WindowModel()
|
||||
|
||||
let commands = app.commands.commands(app: app, window: window)
|
||||
let ssh = try #require(commands.first { $0.target == .connectHost(host.id, .sshSession) })
|
||||
let telnet = try #require(commands.first { $0.target == .connectHost(host.id, .telnetSession) })
|
||||
#expect(ssh.symbol == MenuSymbol.ssh)
|
||||
#expect(telnet.symbol == MenuSymbol.telnet)
|
||||
#expect(ssh.symbol != telnet.symbol)
|
||||
|
||||
let entries = HostMenuEntries.entries(for: host, app: app, window: window)
|
||||
let connect = try #require(entries.first { $0.title == String(localized: "Connect") })
|
||||
let telnetEntry = try #require(
|
||||
connect.children.first { $0.title == String(localized: "with Telnet") })
|
||||
#expect(telnetEntry.symbol == MenuSymbol.telnet)
|
||||
}
|
||||
|
||||
@Test
|
||||
func registryCombinesManagementWorkspacesHostsAndActions() {
|
||||
let host = Host(name: "Production", host: "prod.example", tags: ["critical"])
|
||||
|
||||
@@ -15,6 +15,17 @@ import struct TermindDomain.Host
|
||||
@Suite
|
||||
@MainActor
|
||||
struct WindowStateTests {
|
||||
@Test
|
||||
func sshAndTelnetPanesUseDistinctProtocolSymbols() {
|
||||
let host = Host(
|
||||
name: "Console", host: "console.example.invalid",
|
||||
telnetEnabled: true)
|
||||
|
||||
#expect(Pane.sshSession(host: host).symbol == MenuSymbol.ssh)
|
||||
#expect(Pane.telnetSession(host: host).symbol == MenuSymbol.telnet)
|
||||
#expect(MenuSymbol.ssh != MenuSymbol.telnet)
|
||||
}
|
||||
|
||||
@Test
|
||||
func assistantManualOrderMovesAcrossPinnedBoundaryAndStaysStableAfterActivity() throws {
|
||||
let first = ConversationModel(updatedAt: Date(timeIntervalSinceReferenceDate: 10))
|
||||
|
||||
Reference in new issue
Block a user