MULTI SESSIONS!!!

terminalviewcontainer has a dict of [handler, terminalView] with a uuid key
each session gets a new uuid, and on disconnect, the session is removed from terminalviewcontainer
sessions list view to reopen sessions
remove multiview
extracted terminalviewcontainer
This commit is contained in:
neon443
2025-06-29 15:40:46 +01:00
parent 870ebb6fcc
commit 331a921499
10 changed files with 133 additions and 20 deletions

View File

@@ -40,6 +40,9 @@
A96BE6A22E10846B00C0FEE9 /* xcodeWWDC.plist in Resources */ = {isa = PBXBuildFile; fileRef = A96BE6962E10846B00C0FEE9 /* xcodeWWDC.plist */; };
A96BE6A42E113D9400C0FEE9 /* ThemeCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96BE6A32E113D9400C0FEE9 /* ThemeCodable.swift */; };
A96BE6A62E113DB000C0FEE9 /* ColorCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96BE6A52E113DB000C0FEE9 /* ColorCodable.swift */; };
A96BE6A82E116E2B00C0FEE9 /* SessionsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96BE6A72E116E2B00C0FEE9 /* SessionsListView.swift */; };
A96BE6AA2E116EC000C0FEE9 /* TerminalViewContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96BE6A92E116EC000C0FEE9 /* TerminalViewContainer.swift */; };
A96BE6AD2E11825800C0FEE9 /* SessionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96BE6AC2E11825800C0FEE9 /* SessionView.swift */; };
A96C6A8A2E0C0B1100F377FE /* SSHState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C6A892E0C0B1100F377FE /* SSHState.swift */; };
A96C6AFE2E0C43B600F377FE /* Keypair.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C6AFD2E0C43B600F377FE /* Keypair.swift */; };
A96C6B002E0C45FE00F377FE /* KeyDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A96C6AFF2E0C45FE00F377FE /* KeyDetailView.swift */; };
@@ -133,6 +136,9 @@
A96BE6962E10846B00C0FEE9 /* xcodeWWDC.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = xcodeWWDC.plist; sourceTree = "<group>"; };
A96BE6A32E113D9400C0FEE9 /* ThemeCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeCodable.swift; sourceTree = "<group>"; };
A96BE6A52E113DB000C0FEE9 /* ColorCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCodable.swift; sourceTree = "<group>"; };
A96BE6A72E116E2B00C0FEE9 /* SessionsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionsListView.swift; sourceTree = "<group>"; };
A96BE6A92E116EC000C0FEE9 /* TerminalViewContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalViewContainer.swift; sourceTree = "<group>"; };
A96BE6AC2E11825800C0FEE9 /* SessionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionView.swift; sourceTree = "<group>"; };
A96C6A892E0C0B1100F377FE /* SSHState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHState.swift; sourceTree = "<group>"; };
A96C6AFD2E0C43B600F377FE /* Keypair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keypair.swift; sourceTree = "<group>"; };
A96C6AFF2E0C45FE00F377FE /* KeyDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyDetailView.swift; sourceTree = "<group>"; };
@@ -190,6 +196,7 @@
A92317292E07113100ECE1E6 /* TerminalController.swift */,
A923172E2E08851200ECE1E6 /* ShellView.swift */,
A923172C2E07138000ECE1E6 /* SSHTerminalDelegate.swift */,
A96BE6A92E116EC000C0FEE9 /* TerminalViewContainer.swift */,
);
path = Terminal;
sourceTree = "<group>";
@@ -253,6 +260,7 @@
isa = PBXGroup;
children = (
A92538C52DEE0742007E0A18 /* ContentView.swift */,
A96BE6AB2E11824B00C0FEE9 /* Sessions */,
A9D8192B2E0E9EA400442D38 /* Themes */,
A98554532E05534F009051BD /* Keys */,
A96C6B042E0C523E00F377FE /* Hosts */,
@@ -311,6 +319,15 @@
path = ci_scripts;
sourceTree = "<group>";
};
A96BE6AB2E11824B00C0FEE9 /* Sessions */ = {
isa = PBXGroup;
children = (
A96BE6A72E116E2B00C0FEE9 /* SessionsListView.swift */,
A96BE6AC2E11825800C0FEE9 /* SessionView.swift */,
);
path = Sessions;
sourceTree = "<group>";
};
A96C6B032E0C523600F377FE /* Misc */ = {
isa = PBXGroup;
children = (
@@ -562,8 +579,10 @@
A93143C02DF61B3200FCD5DB /* Host.swift in Sources */,
A9B15A9A2E0ABA0400F66E02 /* DialogView.swift in Sources */,
A92538C92DEE0742007E0A18 /* ShhShellApp.swift in Sources */,
A96BE6AD2E11825800C0FEE9 /* SessionView.swift in Sources */,
A96C6B002E0C45FE00F377FE /* KeyDetailView.swift in Sources */,
A9DA97712E0D30ED00142DDC /* HostSymbol.swift in Sources */,
A96BE6A82E116E2B00C0FEE9 /* SessionsListView.swift in Sources */,
A98554612E058433009051BD /* HostsManager.swift in Sources */,
A985545D2E055D4D009051BD /* ConnectionView.swift in Sources */,
A98554592E0553AA009051BD /* KeyManager.swift in Sources */,
@@ -573,6 +592,7 @@
A923172D2E07138000ECE1E6 /* SSHTerminalDelegate.swift in Sources */,
A96C6AFE2E0C43B600F377FE /* Keypair.swift in Sources */,
A9C4140C2E096DB7005E3047 /* SSHError.swift in Sources */,
A96BE6AA2E116EC000C0FEE9 /* TerminalViewContainer.swift in Sources */,
A923172A2E07113100ECE1E6 /* TerminalController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -9,11 +9,14 @@ import Foundation
import LibSSH
import OSLog
import SwiftUI
import SwiftTerm
class SSHHandler: @unchecked Sendable, ObservableObject {
private var session: ssh_session?
private var channel: ssh_channel?
var sessionID: UUID?
var scrollback: [String] = []
var scrollbackSize = 0.0
@@ -101,6 +104,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
func connect() throws(SSHError) {
guard !host.address.isEmpty else { throw .connectionFailed("No address to connect to.") }
withAnimation { state = .connecting }
sessionID = UUID()
var verbosity: Int = 0
// var verbosity: Int = SSH_LOG_FUNCTIONS
@@ -134,6 +138,12 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
withAnimation { self.testSuceeded = nil }
}
if let sessionID {
Task { @MainActor in
TerminalViewContainer.shared.removeValue(forKey: sessionID)
self.sessionID = nil
}
}
scrollback = []
scrollbackSize = 0

View File

@@ -15,6 +15,12 @@ struct ContentView: View {
var body: some View {
NavigationStack {
List {
SessionsListView(
handler: handler,
hostsManager: hostsManager,
keyManager: keyManager
)
HostsView(
handler: handler,
hostsManager: hostsManager,

View File

@@ -147,7 +147,6 @@ struct ConnectionView: View {
Button() {
handler.go()
showTerminal = checkShell(handler.state)
TerminalController.TerminalViewContainer.shared = nil
} label: {
Label(
handler.connected ? "Disconnect" : "Connect",

View File

@@ -17,17 +17,6 @@ struct HostsView: View {
Text("Add your first Host!")
}
//proves that u can connect to multiple at the same time
NavigationLink() {
ForEach(hostsManager.hosts) { host in
let miniHandler = SSHHandler(host: host)
TerminalController(handler: miniHandler, hostsManager: hostsManager)
.onAppear { miniHandler.go() }
}
} label: {
Label("multiview", systemImage: "square.split.2x2")
}
ForEach(hostsManager.hosts) { host in
NavigationLink() {
ConnectionView(

View File

@@ -0,0 +1,34 @@
//
// SessionView.swift
// ShhShell
//
// Created by neon443 on 29/06/2025.
//
import SwiftUI
struct SessionView: View {
@ObservedObject var hostsManager: HostsManager
@State var key: UUID
@State var shellPresented: Bool = false
var body: some View {
Text(key.uuidString)
.onTapGesture {
shellPresented.toggle()
}
.fullScreenCover(isPresented: $shellPresented) {
ShellView(
handler: TerminalViewContainer.shared[key]!.handler,
hostsManager: hostsManager
)
}
}
}
#Preview {
SessionView(
hostsManager: HostsManager(),
key: UUID()
)
}

View File

@@ -0,0 +1,31 @@
//
// SessionsView.swift
// ShhShell
//
// Created by neon443 on 29/06/2025.
//
import SwiftUI
import SwiftTerm
struct SessionsListView: View {
@ObservedObject var handler: SSHHandler
@ObservedObject var hostsManager: HostsManager
@ObservedObject var keyManager: KeyManager
var body: some View {
Section("Sessions") {
ForEach(TerminalViewContainer.shared.map {$0.key}, id: \.self) { key in
SessionView(hostsManager: hostsManager, key: key)
}
}
}
}
#Preview {
SessionsListView(
handler: SSHHandler(host: Host.debug),
hostsManager: HostsManager(),
keyManager: KeyManager()
)
}

View File

@@ -18,7 +18,9 @@ struct ShellView: View {
ZStack {
TerminalController(handler: handler, hostsManager: hostsManager)
.onAppear {
TerminalController.TerminalViewContainer.shared?.restoreScrollback()
if let sessionID = handler.sessionID {
TerminalViewContainer.shared[sessionID]?.terminalView.restoreScrollback()
}
}
Group {

View File

@@ -14,13 +14,11 @@ struct TerminalController: UIViewRepresentable {
@ObservedObject var handler: SSHHandler
@ObservedObject var hostsManager: HostsManager
final class TerminalViewContainer {
@MainActor static var shared: SSHTerminalDelegate?
}
func makeUIView(context: Context) -> TerminalView {
if let existing = TerminalViewContainer.shared {
return existing
if let sessionID = handler.sessionID {
if let existing = TerminalViewContainer.shared[sessionID] {
return existing.terminalView
}
}
let tv = SSHTerminalDelegate(
@@ -31,7 +29,12 @@ struct TerminalController: UIViewRepresentable {
tv.translatesAutoresizingMaskIntoConstraints = false
tv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
TerminalViewContainer.shared = tv
if let sessionID = handler.sessionID {
TerminalViewContainer.shared[sessionID] = TerminalContainer(
handler: handler,
terminalView: tv
)
}
return tv
}

View File

@@ -0,0 +1,19 @@
//
// TerminalViewContainer.swift
// ShhShell
//
// Created by neon443 on 29/06/2025.
//
import Foundation
public final class TerminalViewContainer {
@MainActor static var shared: [
UUID: TerminalContainer
] = [:]
}
struct TerminalContainer {
var handler: SSHHandler
var terminalView: SSHTerminalDelegate
}