mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 21:36:17 +00:00
tried to implement resuming
This commit is contained in:
@@ -36,10 +36,15 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateHost(_ updatedHost: Host) {
|
func updateHost(_ updatedHost: Host) {
|
||||||
|
let oldID = updatedHost.id
|
||||||
|
|
||||||
if let index = savedHosts.firstIndex(where: { $0.id == updatedHost.id }) {
|
if let index = savedHosts.firstIndex(where: { $0.id == updatedHost.id }) {
|
||||||
var updateHostWithNewID = updatedHost
|
var updateHostWithNewID = updatedHost
|
||||||
updateHostWithNewID.id = UUID()
|
updateHostWithNewID.id = UUID()
|
||||||
withAnimation { savedHosts[index] = updateHostWithNewID }
|
withAnimation { savedHosts[index] = updateHostWithNewID }
|
||||||
|
|
||||||
|
updateHostWithNewID.id = oldID
|
||||||
|
withAnimation { savedHosts[index] = updateHostWithNewID }
|
||||||
saveSavedHosts()
|
saveSavedHosts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
private var session: ssh_session?
|
private var session: ssh_session?
|
||||||
private var channel: ssh_channel?
|
private var channel: ssh_channel?
|
||||||
|
|
||||||
|
var scrollback: [String] = []
|
||||||
|
|
||||||
@Published var title: String = ""
|
@Published var title: String = ""
|
||||||
@Published var state: SSHState = .idle
|
@Published var state: SSHState = .idle
|
||||||
var connected: Bool {
|
var connected: Bool {
|
||||||
@@ -393,6 +395,9 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
// print(String(data: Data(bytes: buffer, count: Int(nbytes)), encoding: .utf8)!)
|
// print(String(data: Data(bytes: buffer, count: Int(nbytes)), encoding: .utf8)!)
|
||||||
#endif
|
#endif
|
||||||
|
Task { @MainActor in
|
||||||
|
scrollback.append(string)
|
||||||
|
}
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
enum SSHState {
|
enum SSHState {
|
||||||
case idle
|
case idle
|
||||||
@@ -16,6 +17,22 @@ enum SSHState {
|
|||||||
|
|
||||||
case connectionFailed
|
case connectionFailed
|
||||||
case authFailed
|
case authFailed
|
||||||
|
|
||||||
|
var color: Color {
|
||||||
|
switch self {
|
||||||
|
case .idle:
|
||||||
|
return .gray
|
||||||
|
|
||||||
|
case .connecting, .authorizing:
|
||||||
|
return .orange
|
||||||
|
|
||||||
|
case .authorized, .shellOpen:
|
||||||
|
return .green
|
||||||
|
|
||||||
|
case .connectionFailed, .authFailed:
|
||||||
|
return .red
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkConnected(_ state: SSHState) -> Bool {
|
func checkConnected(_ state: SSHState) -> Bool {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ struct ConnectionView: View {
|
|||||||
@ObservedObject var hostsManager: HostsManager
|
@ObservedObject var hostsManager: HostsManager
|
||||||
@ObservedObject var keyManager: KeyManager
|
@ObservedObject var keyManager: KeyManager
|
||||||
|
|
||||||
|
@State var resuming: Bool = false
|
||||||
|
|
||||||
@State var passphrase: String = ""
|
@State var passphrase: String = ""
|
||||||
|
|
||||||
@State var pubkeyStr: String = ""
|
@State var pubkeyStr: String = ""
|
||||||
@@ -54,14 +56,8 @@ struct ConnectionView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section {
|
Section {
|
||||||
HStack {
|
Text("\(handler.state)")
|
||||||
Text(handler.connected ? "connected" : "not connected")
|
.foregroundStyle(handler.state.color)
|
||||||
.modifier(foregroundColorStyle(handler.connected ? .green : .red))
|
|
||||||
|
|
||||||
Text(checkAuth(handler.state) ? "authorized" : "unauthorized")
|
|
||||||
.modifier(foregroundColorStyle(checkAuth(handler.state) ? .green : .red))
|
|
||||||
Text("\(handler.state)")
|
|
||||||
}
|
|
||||||
|
|
||||||
TextField("name", text: $handler.host.name)
|
TextField("name", text: $handler.host.name)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
@@ -116,6 +112,7 @@ struct ConnectionView: View {
|
|||||||
|
|
||||||
Button() {
|
Button() {
|
||||||
showTerminal.toggle()
|
showTerminal.toggle()
|
||||||
|
resuming = true
|
||||||
} label: {
|
} label: {
|
||||||
Label("Show Terminal", systemImage: "apple.terminal")
|
Label("Show Terminal", systemImage: "apple.terminal")
|
||||||
}
|
}
|
||||||
@@ -162,7 +159,7 @@ struct ConnectionView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $showTerminal) {
|
.fullScreenCover(isPresented: $showTerminal) {
|
||||||
ShellView(handler: handler)
|
ShellView(handler: handler, resuming: resuming)
|
||||||
}
|
}
|
||||||
.onChange(of: handler.host.key) { _ in
|
.onChange(of: handler.host.key) { _ in
|
||||||
guard let previousKnownHost = hostsManager.getHostMatching(handler.host) else { return }
|
guard let previousKnownHost = hostsManager.getHostMatching(handler.host) else { return }
|
||||||
@@ -172,7 +169,7 @@ struct ConnectionView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
hostsManager.updateHost(handler.host)
|
// hostsManager.updateHost(handler.host)
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
if let publicKeyData = handler.host.publicKey {
|
if let publicKeyData = handler.host.publicKey {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct HostsView: View {
|
|||||||
NavigationLink() {
|
NavigationLink() {
|
||||||
ForEach(hostsManager.savedHosts) { host in
|
ForEach(hostsManager.savedHosts) { host in
|
||||||
let miniHandler = SSHHandler(host: host)
|
let miniHandler = SSHHandler(host: host)
|
||||||
TerminalController(handler: miniHandler)
|
TerminalController(handler: miniHandler, resuming: false)
|
||||||
.onAppear { miniHandler.go() }
|
.onAppear { miniHandler.go() }
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ struct KeyManagerView: View {
|
|||||||
}
|
}
|
||||||
ForEach(hostsManager.savedHosts) { host in
|
ForEach(hostsManager.savedHosts) { host in
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(host.name + "\n" + host.address)
|
if !host.name.isEmpty {
|
||||||
|
Text(host.name)
|
||||||
|
.bold()
|
||||||
|
}
|
||||||
|
Text(host.address)
|
||||||
.bold()
|
.bold()
|
||||||
Text(host.key ?? "nil")
|
Text(host.key ?? "nil")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,25 +12,34 @@ import SwiftTerm
|
|||||||
@MainActor
|
@MainActor
|
||||||
final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalViewDelegate {
|
final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalViewDelegate {
|
||||||
var handler: SSHHandler?
|
var handler: SSHHandler?
|
||||||
var sshQueue: DispatchQueue
|
var sshQueue = DispatchQueue(label: "sshQueue")
|
||||||
|
var resuming: Bool
|
||||||
|
|
||||||
public convenience init(frame: CGRect, handler: SSHHandler) {
|
public convenience init(frame: CGRect, handler: SSHHandler, resuming: Bool) {
|
||||||
self.init(frame: frame)
|
self.init(frame: frame)
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
}
|
self.resuming = resuming
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
|
||||||
sshQueue = DispatchQueue(label: "sshQueue")
|
|
||||||
|
|
||||||
super.init(frame: frame)
|
|
||||||
terminalDelegate = self
|
|
||||||
|
|
||||||
|
sshQueue.async {
|
||||||
|
Task {
|
||||||
|
if resuming {
|
||||||
|
if let handler = await self.handler {
|
||||||
|
for chunk in handler.scrollback {
|
||||||
|
await MainActor.run {
|
||||||
|
self.feed(text: chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sshQueue.async {
|
sshQueue.async {
|
||||||
Task {
|
Task {
|
||||||
guard let handler = await self.handler else { return }
|
guard let handler = await self.handler else { return }
|
||||||
while handler.connected {
|
while handler.connected {
|
||||||
if let read = handler.readFromChannel() {
|
if let read = handler.readFromChannel() {
|
||||||
Task { @MainActor in
|
await MainActor.run {
|
||||||
self.feed(text: read)
|
self.feed(text: read)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -42,6 +51,12 @@ final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalVie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override init(frame: CGRect) {
|
||||||
|
self.resuming = false
|
||||||
|
super.init(frame: frame)
|
||||||
|
terminalDelegate = self
|
||||||
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("unimplemented")
|
fatalError("unimplemented")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import SwiftUI
|
|||||||
|
|
||||||
struct ShellView: View {
|
struct ShellView: View {
|
||||||
@ObservedObject var handler: SSHHandler
|
@ObservedObject var handler: SSHHandler
|
||||||
|
@State var resuming: Bool = false
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
@State private var terminalControllerRef: TerminalController?
|
@State private var terminalControllerRef: TerminalController?
|
||||||
@@ -31,7 +33,7 @@ struct ShellView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
terminalControllerRef = TerminalController(handler: handler)
|
terminalControllerRef = TerminalController(handler: handler, resuming: resuming)
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
@@ -43,13 +45,13 @@ struct ShellView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO: FIX
|
//TODO: FIX
|
||||||
// ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
// Button() {
|
Button() {
|
||||||
// dismiss()
|
dismiss()
|
||||||
// } label: {
|
} label: {
|
||||||
// Label("Close", systemImage: "arrow.down.right.and.arrow.up.left")
|
Label("Close", systemImage: "arrow.down.right.and.arrow.up.left")
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: handler.connected) { _ in
|
.onChange(of: handler.connected) { _ in
|
||||||
if !handler.connected { dismiss() }
|
if !handler.connected { dismiss() }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import SwiftTerm
|
|||||||
|
|
||||||
struct TerminalController: UIViewRepresentable {
|
struct TerminalController: UIViewRepresentable {
|
||||||
@ObservedObject var handler: SSHHandler
|
@ObservedObject var handler: SSHHandler
|
||||||
|
@State var resuming: Bool
|
||||||
|
|
||||||
func makeUIView(context: Context) -> TerminalView {
|
func makeUIView(context: Context) -> TerminalView {
|
||||||
let tv = SSHTerminalView(
|
let tv = SSHTerminalView(
|
||||||
@@ -19,7 +20,8 @@ struct TerminalController: UIViewRepresentable {
|
|||||||
origin: CGPoint(x: 0, y: 0),
|
origin: CGPoint(x: 0, y: 0),
|
||||||
size: .zero
|
size: .zero
|
||||||
),
|
),
|
||||||
handler: handler
|
handler: handler,
|
||||||
|
resuming: resuming
|
||||||
)
|
)
|
||||||
|
|
||||||
tv.translatesAutoresizingMaskIntoConstraints = false
|
tv.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|||||||
Reference in New Issue
Block a user