mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
added a prompt to TextBox
fix right alignment in textbox added quick connect fix blank keys being returned from getKeys() safely unwrap keys in keydetailview swap buttons on shellview's titlbar
This commit is contained in:
@@ -185,6 +185,7 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
func getKeys() -> [Keypair] {
|
func getKeys() -> [Keypair] {
|
||||||
var result: [Keypair] = []
|
var result: [Keypair] = []
|
||||||
for host in hosts {
|
for host in hosts {
|
||||||
|
guard host.privateKey != nil && host.publicKey != nil else { continue }
|
||||||
let keypair = Keypair(publicKey: host.publicKey, privateKey: host.privateKey)
|
let keypair = Keypair(publicKey: host.publicKey, privateKey: host.privateKey)
|
||||||
if !result.contains(keypair) {
|
if !result.contains(keypair) {
|
||||||
result.append(keypair)
|
result.append(keypair)
|
||||||
|
|||||||
@@ -51,34 +51,35 @@ struct ConnectionView: View {
|
|||||||
.id(handler.host)
|
.id(handler.host)
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
|
|
||||||
TextBox(label: "Icon Text", text: $handler.host.label)
|
TextBox(label: "Icon Text", text: $handler.host.label, prompt: "a few letters in the icon")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section {
|
Section {
|
||||||
Text("\(handler.state)")
|
Text("\(handler.state)")
|
||||||
.foregroundStyle(handler.state.color)
|
.foregroundStyle(handler.state.color)
|
||||||
|
|
||||||
TextBox(label: "Name", text: $handler.host.name)
|
TextBox(label: "Name", text: $handler.host.name, prompt: "defaults to host address")
|
||||||
|
|
||||||
TextBox(label: "Address", text: $handler.host.address)
|
TextBox(label: "Address", text: $handler.host.address, prompt: "required")
|
||||||
|
|
||||||
TextBox(label: "Port", text: Binding(
|
TextBox(label: "Port", text: Binding(
|
||||||
get: { String(handler.host.port) },
|
get: { String(handler.host.port) },
|
||||||
set: {
|
set: {
|
||||||
if let input = Int($0) {
|
if let input = Int($0) {
|
||||||
handler.host.port = input
|
handler.host.port = input
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
prompt: "most likely 22",
|
||||||
keyboardType: .numberPad
|
keyboardType: .numberPad
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
TextBox(label: "Username", text: $handler.host.username)
|
TextBox(label: "Username", text: $handler.host.username, prompt: "required")
|
||||||
|
|
||||||
TextBox(label: "Password", text: $handler.host.password, secure: true)
|
TextBox(label: "Password", text: $handler.host.password, prompt: "not required if using publickeys", secure: true)
|
||||||
|
|
||||||
TextBox(label: "Publickey", text: $pubkeyStr)
|
TextBox(label: "Publickey", text: $pubkeyStr, prompt: "in openssh format")
|
||||||
.onChange(of: pubkeyStr) { _ in
|
.onChange(of: pubkeyStr) { _ in
|
||||||
let newStr = pubkeyStr.replacingOccurrences(of: "\r\n", with: "")
|
let newStr = pubkeyStr.replacingOccurrences(of: "\r\n", with: "")
|
||||||
handler.host.publicKey = Data(newStr.utf8)
|
handler.host.publicKey = Data(newStr.utf8)
|
||||||
@@ -88,7 +89,7 @@ struct ConnectionView: View {
|
|||||||
handler.host.publicKey = Data(newStr.utf8)
|
handler.host.publicKey = Data(newStr.utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBox(label: "Privatekey", text: $privkeyStr, secure: true)
|
TextBox(label: "Privatekey", text: $privkeyStr, prompt: "required if using publickeys", secure: true)
|
||||||
.onSubmit {
|
.onSubmit {
|
||||||
let newStr = privkeyStr.replacingOccurrences(of: "\r\n", with: "")
|
let newStr = privkeyStr.replacingOccurrences(of: "\r\n", with: "")
|
||||||
handler.host.privateKey = Data(newStr.utf8)
|
handler.host.privateKey = Data(newStr.utf8)
|
||||||
@@ -98,7 +99,7 @@ struct ConnectionView: View {
|
|||||||
handler.host.privateKey = Data(newStr.utf8)
|
handler.host.privateKey = Data(newStr.utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
TextBox(label: "Passphrase", text: $handler.host.passphrase)
|
TextBox(label: "Passphrase", text: $handler.host.passphrase, prompt: "optional")
|
||||||
}
|
}
|
||||||
|
|
||||||
Button() {
|
Button() {
|
||||||
|
|||||||
@@ -32,6 +32,24 @@ struct HostsView: View {
|
|||||||
}
|
}
|
||||||
.id(host)
|
.id(host)
|
||||||
.animation(.default, value: host)
|
.animation(.default, value: host)
|
||||||
|
.fullScreenCover(
|
||||||
|
isPresented: Binding(
|
||||||
|
get: { checkShell(handler.state) },
|
||||||
|
set: { newValue in
|
||||||
|
handler.go()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ShellView(handler: handler, hostsManager: hostsManager)
|
||||||
|
}
|
||||||
|
.swipeActions(edge: .leading) {
|
||||||
|
Button() {
|
||||||
|
handler.go()
|
||||||
|
} label: {
|
||||||
|
Label("Quick Connect", systemImage: "power")
|
||||||
|
}
|
||||||
|
.foregroundStyle(.green)
|
||||||
|
}
|
||||||
.swipeActions(edge: .trailing) {
|
.swipeActions(edge: .trailing) {
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
hostsManager.removeHost(host)
|
hostsManager.removeHost(host)
|
||||||
@@ -43,6 +61,7 @@ struct HostsView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Label("Duplicate", systemImage: "square.filled.on.square")
|
Label("Duplicate", systemImage: "square.filled.on.square")
|
||||||
}
|
}
|
||||||
|
.foregroundStyle(.blue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onMove(perform: {
|
.onMove(perform: {
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ struct KeyDetailView: View {
|
|||||||
@State var keypair: Keypair
|
@State var keypair: Keypair
|
||||||
@State private var reveal: Bool = false
|
@State private var reveal: Bool = false
|
||||||
|
|
||||||
|
var publicKey: Data {
|
||||||
|
return keypair.publicKey ?? "".data(using: .utf8)!
|
||||||
|
}
|
||||||
|
var privateKey: Data {
|
||||||
|
return keypair.privateKey ?? "".data(using: .utf8)!
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
hostsManager.selectedTheme.background.suiColor.opacity(0.7)
|
hostsManager.selectedTheme.background.suiColor.opacity(0.7)
|
||||||
@@ -31,14 +38,14 @@ struct KeyDetailView: View {
|
|||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Public key")
|
Text("Public key")
|
||||||
.bold()
|
.bold()
|
||||||
Text(String(data: keypair.publicKey!, encoding: .utf8) ?? "nil")
|
Text(String(data: publicKey, encoding: .utf8) ?? "nil")
|
||||||
}
|
}
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Private key")
|
Text("Private key")
|
||||||
.bold()
|
.bold()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
ZStack(alignment: .center) {
|
ZStack(alignment: .center) {
|
||||||
Text(String(data: keypair.privateKey!, encoding: .utf8) ?? "nil")
|
Text(String(data: privateKey, encoding: .utf8) ?? "nil")
|
||||||
.blur(radius: reveal ? 0 : 5)
|
.blur(radius: reveal ? 0 : 5)
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "eye.slash.fill")
|
Image(systemName: "eye.slash.fill")
|
||||||
@@ -62,9 +69,7 @@ struct KeyDetailView: View {
|
|||||||
Button {
|
Button {
|
||||||
Task {
|
Task {
|
||||||
guard await hostsManager.authWithBiometrics() else { return }
|
guard await hostsManager.authWithBiometrics() else { return }
|
||||||
if let privateKey = keypair.privateKey {
|
UIPasteboard.general.string = String(data: privateKey, encoding: .utf8)
|
||||||
UIPasteboard.general.string = String(data: privateKey, encoding: .utf8)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
CenteredLabel(title: "Copy private key", systemName: "document.on.document")
|
CenteredLabel(title: "Copy private key", systemName: "document.on.document")
|
||||||
|
|||||||
@@ -10,22 +10,24 @@ import SwiftUI
|
|||||||
struct TextBox: View {
|
struct TextBox: View {
|
||||||
@State var label: String
|
@State var label: String
|
||||||
@Binding var text: String
|
@Binding var text: String
|
||||||
|
@State var prompt: String
|
||||||
@State var secure: Bool = false
|
@State var secure: Bool = false
|
||||||
@State var keyboardType: UIKeyboardType = .default
|
@State var keyboardType: UIKeyboardType = .default
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Text(label)
|
Text(label)
|
||||||
Spacer()
|
|
||||||
if secure {
|
if secure {
|
||||||
SecureField("", text: $text)
|
SecureField("", text: $text, prompt: Text(prompt))
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
} else {
|
} else {
|
||||||
TextField("", text: $text)
|
TextField("", text: $text, prompt: Text(prompt))
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
TextBox(label: "Label", text: .constant("asdflkajsdl"))
|
TextBox(label: "Label", text: .constant("asdflkajsdl"), prompt: "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,13 +47,6 @@ struct ShellView: View {
|
|||||||
.navigationTitle(handler.title)
|
.navigationTitle(handler.title)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
|
||||||
Button() {
|
|
||||||
dismiss()
|
|
||||||
} label: {
|
|
||||||
Label("Close", systemImage: "arrow.down.right.and.arrow.up.left")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
Button() {
|
Button() {
|
||||||
handler.disconnect()
|
handler.disconnect()
|
||||||
@@ -62,6 +55,13 @@ struct ShellView: View {
|
|||||||
Label("Disconnect", systemImage: "xmark.app.fill")
|
Label("Disconnect", systemImage: "xmark.app.fill")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
|
Button() {
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
Label("Close", systemImage: "arrow.down.right.and.arrow.up.left")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user