mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
implement scrollback saving
implement a scrollback cap of 10MiB add prettyscrollback() to return kib, mib etc tried a different resuming thingy
This commit is contained in:
@@ -15,6 +15,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
private var channel: ssh_channel?
|
private var channel: ssh_channel?
|
||||||
|
|
||||||
var scrollback: [String] = []
|
var scrollback: [String] = []
|
||||||
|
var scrollbackSize = 0.0
|
||||||
|
|
||||||
@Published var title: String = ""
|
@Published var title: String = ""
|
||||||
@Published var state: SSHState = .idle
|
@Published var state: SSHState = .idle
|
||||||
@@ -133,6 +134,9 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
withAnimation { self.testSuceeded = nil }
|
withAnimation { self.testSuceeded = nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrollback = []
|
||||||
|
scrollbackSize = 0
|
||||||
|
|
||||||
//send eof if open
|
//send eof if open
|
||||||
if ssh_channel_is_open(channel) == 1 {
|
if ssh_channel_is_open(channel) == 1 {
|
||||||
ssh_channel_send_eof(channel)
|
ssh_channel_send_eof(channel)
|
||||||
@@ -396,7 +400,12 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
// 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
|
Task { @MainActor in
|
||||||
scrollback.append(string)
|
scrollback.append(string)
|
||||||
|
if scrollbackSize/1024/1024 > 10 {
|
||||||
|
scrollback.remove(at: 0)
|
||||||
|
} else {
|
||||||
|
scrollbackSize += Double(string.lengthOfBytes(using: .utf8))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return string
|
return string
|
||||||
}
|
}
|
||||||
@@ -429,6 +438,16 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
// print("resized tty to \(toRows)rows and \(toCols)cols")
|
// print("resized tty to \(toRows)rows and \(toCols)cols")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prettyScrollbackSize() -> String {
|
||||||
|
if (scrollbackSize/1024/1024) > 1 {
|
||||||
|
return "\(scrollbackSize/1024/1024) MiB scrollback"
|
||||||
|
} else if scrollbackSize/1024 > 1 {
|
||||||
|
return "\(scrollbackSize/1024) KiB scrollback"
|
||||||
|
} else {
|
||||||
|
return "\(scrollbackSize) B scrollback"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func logSshGetError() {
|
private func logSshGetError() {
|
||||||
guard var session = self.session else { return }
|
guard var session = self.session else { return }
|
||||||
logger.critical("\(String(cString: ssh_get_error(&session)))")
|
logger.critical("\(String(cString: ssh_get_error(&session)))")
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ 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 = ""
|
||||||
@@ -112,7 +110,6 @@ 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")
|
||||||
}
|
}
|
||||||
@@ -159,7 +156,7 @@ struct ConnectionView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $showTerminal) {
|
.fullScreenCover(isPresented: $showTerminal) {
|
||||||
ShellView(handler: handler, resuming: resuming)
|
ShellView(handler: handler)
|
||||||
}
|
}
|
||||||
.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 }
|
||||||
@@ -169,7 +166,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, resuming: false)
|
TerminalController(handler: miniHandler)
|
||||||
.onAppear { miniHandler.go() }
|
.onAppear { miniHandler.go() }
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
|
|||||||
@@ -12,29 +12,15 @@ 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(label: "sshQueue")
|
// var sshQueue = DispatchQueue(label: "sshQueue")
|
||||||
var resuming: Bool
|
|
||||||
|
|
||||||
public convenience init(frame: CGRect, handler: SSHHandler, resuming: Bool) {
|
public convenience init(frame: CGRect, handler: SSHHandler) {
|
||||||
self.init(frame: frame)
|
self.init(frame: frame)
|
||||||
self.handler = handler
|
self.handler = handler
|
||||||
self.resuming = resuming
|
|
||||||
|
|
||||||
sshQueue.async {
|
restoreScrollback()
|
||||||
Task {
|
|
||||||
if resuming {
|
DispatchQueue.main.async {
|
||||||
if let handler = await self.handler {
|
|
||||||
for chunk in handler.scrollback {
|
|
||||||
await MainActor.run {
|
|
||||||
self.feed(text: chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
@@ -52,7 +38,6 @@ final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
self.resuming = false
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
terminalDelegate = self
|
terminalDelegate = self
|
||||||
}
|
}
|
||||||
@@ -100,4 +85,16 @@ final class SSHTerminalView: TerminalView, Sendable, @preconcurrency TerminalVie
|
|||||||
public func bell(source: TerminalView) {
|
public func bell(source: TerminalView) {
|
||||||
handler?.ring()
|
handler?.ring()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restoreScrollback() {
|
||||||
|
guard let scrollback = handler?.scrollback else { return }
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||||
|
for line in scrollback {
|
||||||
|
self.feed(text: line)
|
||||||
|
}
|
||||||
|
self.setNeedsLayout()
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ 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
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ struct ShellView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
terminalControllerRef = TerminalController(handler: handler, resuming: resuming)
|
terminalControllerRef = TerminalController(handler: handler)
|
||||||
}
|
}
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ 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(
|
||||||
@@ -20,8 +19,7 @@ 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