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