terminalViewContainer is now an observable object

made the session list view more pretty
terminalviewcontaner has a static shared property which is an instance of itself
then it has sessions inside with a dict of uuid:terminalcontainer inside
hostsview is in the section "hosts"
This commit is contained in:
neon443
2025-06-29 16:25:49 +01:00
parent 331a921499
commit 8e69dfc165
9 changed files with 82 additions and 48 deletions

View File

@@ -341,8 +341,8 @@
A96C6B042E0C523E00F377FE /* Hosts */ = {
isa = PBXGroup;
children = (
A985545C2E055D4D009051BD /* ConnectionView.swift */,
A98554622E0587DF009051BD /* HostsView.swift */,
A985545C2E055D4D009051BD /* ConnectionView.swift */,
);
path = Hosts;
sourceTree = "<group>";

View File

@@ -133,7 +133,8 @@ class HostsManager: ObservableObject, @unchecked Sendable {
}
}
func makeLabel(forHost: Host) -> String {
func makeLabel(forHost: Host?) -> String {
guard let forHost else { return "" }
if forHost.name.isEmpty && forHost.address.isEmpty {
return forHost.id.uuidString
} else if forHost.name.isEmpty {

View File

@@ -15,6 +15,9 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
private var session: ssh_session?
private var channel: ssh_channel?
@MainActor var container: TerminalViewContainer {
TerminalViewContainer.shared
}
var sessionID: UUID?
var scrollback: [String] = []
@@ -140,7 +143,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
if let sessionID {
Task { @MainActor in
TerminalViewContainer.shared.removeValue(forKey: sessionID)
container.sessions.removeValue(forKey: sessionID)
self.sessionID = nil
}
}

View File

@@ -17,36 +17,38 @@ struct HostsView: View {
Text("Add your first Host!")
}
ForEach(hostsManager.hosts) { host in
NavigationLink() {
ConnectionView(
handler: SSHHandler(host: host),
hostsManager: hostsManager,
keyManager: keyManager
)
} label: {
SymbolPreview(symbol: host.symbol, label: host.label)
.frame(width: 40, height: 40)
Text(hostsManager.makeLabel(forHost: host))
}
.id(host)
.animation(.default, value: host)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
hostsManager.removeHost(host)
Section("Hosts") {
ForEach(hostsManager.hosts) { host in
NavigationLink() {
ConnectionView(
handler: SSHHandler(host: host),
hostsManager: hostsManager,
keyManager: keyManager
)
} label: {
Label("Delete", systemImage: "trash")
SymbolPreview(symbol: host.symbol, label: host.label)
.frame(width: 40, height: 40)
Text(hostsManager.makeLabel(forHost: host))
}
Button() {
hostsManager.duplicateHost(host)
} label: {
Label("Duplicate", systemImage: "square.filled.on.square")
.id(host)
.animation(.default, value: host)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
hostsManager.removeHost(host)
} label: {
Label("Delete", systemImage: "trash")
}
Button() {
hostsManager.duplicateHost(host)
} label: {
Label("Duplicate", systemImage: "square.filled.on.square")
}
}
}
.onMove(perform: {
hostsManager.moveHost(from: $0, to: $1)
})
}
.onMove(perform: {
hostsManager.moveHost(from: $0, to: $1)
})
Section() {
NavigationLink {

View File

@@ -9,20 +9,34 @@ import SwiftUI
struct SessionView: View {
@ObservedObject var hostsManager: HostsManager
@ObservedObject var container = TerminalViewContainer.shared
@State var key: UUID
@State var shellPresented: Bool = false
var host: Host {
container.sessions[key]?.handler.host ?? Host.blank
}
var body: some View {
Text(key.uuidString)
.onTapGesture {
shellPresented.toggle()
}
.fullScreenCover(isPresented: $shellPresented) {
ShellView(
handler: TerminalViewContainer.shared[key]!.handler,
hostsManager: hostsManager
)
}
HStack {
Image(systemName: "apple.terminal")
.resizable().scaledToFit()
.frame(width: 40, height: 40)
.foregroundStyle(.terminalGreen)
SymbolPreview(symbol: host.symbol, label: host.label)
.frame(width: 40, height: 40)
Text(hostsManager.makeLabel(forHost: host))
}
.onTapGesture {
shellPresented.toggle()
}
.fullScreenCover(isPresented: $shellPresented) {
ShellView(
handler: container.sessions[key]?.handler ?? SSHHandler(host: Host.blank),
hostsManager: hostsManager
)
}
}
}

View File

@@ -13,10 +13,15 @@ struct SessionsListView: View {
@ObservedObject var hostsManager: HostsManager
@ObservedObject var keyManager: KeyManager
@ObservedObject var container = TerminalViewContainer.shared
var body: some View {
Section("Sessions") {
ForEach(TerminalViewContainer.shared.map {$0.key}, id: \.self) { key in
SessionView(hostsManager: hostsManager, key: key)
if !container.sessions.isEmpty {
Section("Sessions") {
ForEach(container.sessionIDs, id: \.self) { key in
SessionView(hostsManager: hostsManager, key: key)
.id(container.sessions[key]!.handler.connected)
}
}
}
}

View File

@@ -11,6 +11,8 @@ struct ShellView: View {
@ObservedObject var handler: SSHHandler
@ObservedObject var hostsManager: HostsManager
@ObservedObject var container = TerminalViewContainer.shared
@Environment(\.dismiss) var dismiss
var body: some View {
@@ -19,7 +21,7 @@ struct ShellView: View {
TerminalController(handler: handler, hostsManager: hostsManager)
.onAppear {
if let sessionID = handler.sessionID {
TerminalViewContainer.shared[sessionID]?.terminalView.restoreScrollback()
container.sessions[sessionID]?.terminalView.restoreScrollback()
}
}

View File

@@ -14,9 +14,11 @@ struct TerminalController: UIViewRepresentable {
@ObservedObject var handler: SSHHandler
@ObservedObject var hostsManager: HostsManager
@ObservedObject var container = TerminalViewContainer.shared
func makeUIView(context: Context) -> TerminalView {
if let sessionID = handler.sessionID {
if let existing = TerminalViewContainer.shared[sessionID] {
if let existing = container.sessions[sessionID] {
return existing.terminalView
}
}
@@ -30,7 +32,7 @@ struct TerminalController: UIViewRepresentable {
tv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
if let sessionID = handler.sessionID {
TerminalViewContainer.shared[sessionID] = TerminalContainer(
container.sessions[sessionID] = TerminalContainer(
handler: handler,
terminalView: tv
)

View File

@@ -7,10 +7,15 @@
import Foundation
public final class TerminalViewContainer {
@MainActor static var shared: [
UUID: TerminalContainer
] = [:]
@MainActor
public final class TerminalViewContainer: ObservableObject {
static let shared = TerminalViewContainer()
@Published var sessions: [UUID: TerminalContainer] = [:]
var sessionIDs: [UUID] {
return sessions.map({ $0.key })
}
}
struct TerminalContainer {