added symbols and labels

added makeLabel() to return a string for the host, in order: name, address, uuid
added reorder sypport
added movehost() to reorder
added symbolpreview view to easilty show the symbol and label
added symbol with sf, iscustom, offset vars to help
added a picker to choose the symbol
This commit is contained in:
neon443
2025-06-26 09:54:54 +01:00
parent 58f4388168
commit 642e4a78de
9 changed files with 288 additions and 10 deletions

View File

@@ -6,10 +6,13 @@
//
import Foundation
import SwiftUI
protocol HostPr: Codable, Identifiable, Equatable {
var id: UUID { get set }
var name: String { get set }
var symbol: Symbol { get set }
var label: String { get set }
var address: String { get set }
var port: Int { get set }
var username: String { get set }
@@ -22,18 +25,22 @@ protocol HostPr: Codable, Identifiable, Equatable {
struct Host: HostPr {
var id = UUID()
var name: String = ""
var address: String = ""
var name: String
var symbol: Symbol
var label: String
var address: String
var port: Int
var username: String
var password: String
var publicKey: Data?
var privateKey: Data?
var passphrase: String = ""
var passphrase: String
var key: String?
init(
name: String = "",
symbol: Symbol = .genericServer,
label: String = "",
address: String,
port: Int = 22,
username: String = "",
@@ -44,6 +51,8 @@ struct Host: HostPr {
hostkey: String? = nil
) {
self.name = name
self.symbol = symbol
self.label = label
self.address = address
self.port = port
self.username = username
@@ -62,6 +71,7 @@ extension Host {
static var debug: Host {
Host(
name: "name for localhost",
label: "lo0",
address: "localhost",
port: 22,
username: "neon443",

View File

@@ -50,6 +50,23 @@ class HostsManager: ObservableObject, @unchecked Sendable {
}
}
func makeLabel(forHost: Host) -> String {
if forHost.name.isEmpty && forHost.address.isEmpty {
return forHost.id.uuidString
} else if forHost.name.isEmpty {
return forHost.address
} else if forHost.address.isEmpty {
return forHost.name
} else {
return forHost.name
}
}
func moveHost(from: IndexSet, to: Int) {
savedHosts.move(fromOffsets: from, toOffset: to)
saveSavedHosts()
}
func loadSavedHosts() {
userDefaults.synchronize()
let decoder = JSONDecoder()

View File

@@ -0,0 +1,64 @@
//
// Symbol.swift
// ShhShell
//
// Created by neon443 on 26/06/2025.
//
import Foundation
import SwiftUI
enum Symbol: Codable, Hashable, Equatable, CaseIterable {
case desktopcomputer
case laptopcomputer
case trashcan
case genericPC
case genericServer
case genericServerVertical
var sf: String {
switch self {
case .desktopcomputer:
return "desktopcomputer"
case .laptopcomputer:
return "laptopcomputer"
case .trashcan:
return "macpro.gen2"
case .genericPC:
return "custom.pc"
case .genericServer:
return "rectangle"
case .genericServerVertical:
return "rectangle.portrait"
}
}
var isCustom: Bool {
switch self {
case .genericPC:
return true
default:
return false
}
}
var offset: CGSize {
var deltaHeight: Double
switch self {
case .desktopcomputer:
deltaHeight = -6
case .laptopcomputer:
deltaHeight = -2
case .genericPC:
deltaHeight = -6
default:
deltaHeight = 0
}
return CGSize(width: 0, height: deltaHeight)
}
}

View File

@@ -0,0 +1,36 @@
//
// SymbolPreview.swift
// ShhShell
//
// Created by neon443 on 26/06/2025.
//
import SwiftUI
struct SymbolPreview: View {
@State var symbol: Symbol
@State var label: String
var body: some View {
ZStack(alignment: .center) {
if symbol.isCustom {
Image(symbol.sf)
.resizable().scaledToFit()
.symbolRenderingMode(.monochrome)
.padding(5)
} else {
Image(systemName: symbol.sf)
.resizable().scaledToFit()
.symbolRenderingMode(.monochrome)
.padding(5)
}
Text(label)
.font(.headline)
.offset(symbol.offset)
}
}
}
#Preview {
SymbolPreview(symbol: Symbol.desktopcomputer, label: "lo0")
}

View File

@@ -24,6 +24,21 @@ struct ConnectionView: View {
var body: some View {
NavigationStack {
List {
Section {
HStack {
Picker("", selection: $handler.host.symbol) {
ForEach(Symbol.allCases, id: \.self) { symbol in
SymbolPreview(symbol: symbol, label: handler.host.label)
.tag(symbol)
.frame(width: 60, height: 60)
}
}
.pickerStyle(SegmentedPickerStyle())
}
.scrollIndicators(.hidden)
TextField("label", text: $handler.host.label)
.textFieldStyle(.roundedBorder)
}
Section {
HStack {
Text(handler.connected ? "connected" : "not connected")
@@ -33,6 +48,7 @@ struct ConnectionView: View {
.modifier(foregroundColorStyle(checkAuth(handler.state) ? .green : .red))
Text("\(handler.state)")
}
TextField("name", text: $handler.host.name)
.textFieldStyle(.roundedBorder)

View File

@@ -44,13 +44,8 @@ struct HostsView: View {
keyManager: keyManager
)
} label: {
if host.name.isEmpty {
Text(host.address)
} else if host.address.isEmpty {
Text(host.name)
} else {
Text(host.id.uuidString)
}
SymbolPreview(symbol: host.symbol, label: host.label)
.frame(width: 30, height: 30)
}
.animation(.default, value: host)
.swipeActions(edge: .trailing) {
@@ -66,6 +61,9 @@ struct HostsView: View {
}
}
}
.onMove(perform: {
hostsManager.moveHost(from: $0, to: $1)
})
}
.transition(.opacity)
.toolbar {