mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -147,7 +147,6 @@ struct ConnectionView: View {
|
||||
Button() {
|
||||
handler.go()
|
||||
showTerminal = checkShell(handler.state)
|
||||
TerminalController.TerminalViewContainer.shared = nil
|
||||
} label: {
|
||||
Label(
|
||||
handler.connected ? "Disconnect" : "Connect",
|
||||
|
||||
@@ -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(
|
||||
|
||||
34
ShhShell/Views/Sessions/SessionView.swift
Normal file
34
ShhShell/Views/Sessions/SessionView.swift
Normal 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()
|
||||
)
|
||||
}
|
||||
31
ShhShell/Views/Sessions/SessionsListView.swift
Normal file
31
ShhShell/Views/Sessions/SessionsListView.swift
Normal 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()
|
||||
)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
19
ShhShell/Views/Terminal/TerminalViewContainer.swift
Normal file
19
ShhShell/Views/Terminal/TerminalViewContainer.swift
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user