mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
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:
@@ -341,8 +341,8 @@
|
|||||||
A96C6B042E0C523E00F377FE /* Hosts */ = {
|
A96C6B042E0C523E00F377FE /* Hosts */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A985545C2E055D4D009051BD /* ConnectionView.swift */,
|
|
||||||
A98554622E0587DF009051BD /* HostsView.swift */,
|
A98554622E0587DF009051BD /* HostsView.swift */,
|
||||||
|
A985545C2E055D4D009051BD /* ConnectionView.swift */,
|
||||||
);
|
);
|
||||||
path = Hosts;
|
path = Hosts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|||||||
@@ -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 {
|
if forHost.name.isEmpty && forHost.address.isEmpty {
|
||||||
return forHost.id.uuidString
|
return forHost.id.uuidString
|
||||||
} else if forHost.name.isEmpty {
|
} else if forHost.name.isEmpty {
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
private var session: ssh_session?
|
private var session: ssh_session?
|
||||||
private var channel: ssh_channel?
|
private var channel: ssh_channel?
|
||||||
|
|
||||||
|
@MainActor var container: TerminalViewContainer {
|
||||||
|
TerminalViewContainer.shared
|
||||||
|
}
|
||||||
var sessionID: UUID?
|
var sessionID: UUID?
|
||||||
|
|
||||||
var scrollback: [String] = []
|
var scrollback: [String] = []
|
||||||
@@ -140,7 +143,7 @@ class SSHHandler: @unchecked Sendable, ObservableObject {
|
|||||||
|
|
||||||
if let sessionID {
|
if let sessionID {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
TerminalViewContainer.shared.removeValue(forKey: sessionID)
|
container.sessions.removeValue(forKey: sessionID)
|
||||||
self.sessionID = nil
|
self.sessionID = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,36 +17,38 @@ struct HostsView: View {
|
|||||||
Text("Add your first Host!")
|
Text("Add your first Host!")
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach(hostsManager.hosts) { host in
|
Section("Hosts") {
|
||||||
NavigationLink() {
|
ForEach(hostsManager.hosts) { host in
|
||||||
ConnectionView(
|
NavigationLink() {
|
||||||
handler: SSHHandler(host: host),
|
ConnectionView(
|
||||||
hostsManager: hostsManager,
|
handler: SSHHandler(host: host),
|
||||||
keyManager: keyManager
|
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)
|
|
||||||
} label: {
|
} label: {
|
||||||
Label("Delete", systemImage: "trash")
|
SymbolPreview(symbol: host.symbol, label: host.label)
|
||||||
|
.frame(width: 40, height: 40)
|
||||||
|
Text(hostsManager.makeLabel(forHost: host))
|
||||||
}
|
}
|
||||||
Button() {
|
.id(host)
|
||||||
hostsManager.duplicateHost(host)
|
.animation(.default, value: host)
|
||||||
} label: {
|
.swipeActions(edge: .trailing) {
|
||||||
Label("Duplicate", systemImage: "square.filled.on.square")
|
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() {
|
Section() {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
|
|||||||
@@ -9,20 +9,34 @@ import SwiftUI
|
|||||||
|
|
||||||
struct SessionView: View {
|
struct SessionView: View {
|
||||||
@ObservedObject var hostsManager: HostsManager
|
@ObservedObject var hostsManager: HostsManager
|
||||||
|
@ObservedObject var container = TerminalViewContainer.shared
|
||||||
|
|
||||||
@State var key: UUID
|
@State var key: UUID
|
||||||
@State var shellPresented: Bool = false
|
@State var shellPresented: Bool = false
|
||||||
|
|
||||||
|
var host: Host {
|
||||||
|
container.sessions[key]?.handler.host ?? Host.blank
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text(key.uuidString)
|
HStack {
|
||||||
.onTapGesture {
|
Image(systemName: "apple.terminal")
|
||||||
shellPresented.toggle()
|
.resizable().scaledToFit()
|
||||||
}
|
.frame(width: 40, height: 40)
|
||||||
.fullScreenCover(isPresented: $shellPresented) {
|
.foregroundStyle(.terminalGreen)
|
||||||
ShellView(
|
SymbolPreview(symbol: host.symbol, label: host.label)
|
||||||
handler: TerminalViewContainer.shared[key]!.handler,
|
.frame(width: 40, height: 40)
|
||||||
hostsManager: hostsManager
|
Text(hostsManager.makeLabel(forHost: host))
|
||||||
)
|
}
|
||||||
}
|
.onTapGesture {
|
||||||
|
shellPresented.toggle()
|
||||||
|
}
|
||||||
|
.fullScreenCover(isPresented: $shellPresented) {
|
||||||
|
ShellView(
|
||||||
|
handler: container.sessions[key]?.handler ?? SSHHandler(host: Host.blank),
|
||||||
|
hostsManager: hostsManager
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,15 @@ struct SessionsListView: View {
|
|||||||
@ObservedObject var hostsManager: HostsManager
|
@ObservedObject var hostsManager: HostsManager
|
||||||
@ObservedObject var keyManager: KeyManager
|
@ObservedObject var keyManager: KeyManager
|
||||||
|
|
||||||
|
@ObservedObject var container = TerminalViewContainer.shared
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section("Sessions") {
|
if !container.sessions.isEmpty {
|
||||||
ForEach(TerminalViewContainer.shared.map {$0.key}, id: \.self) { key in
|
Section("Sessions") {
|
||||||
SessionView(hostsManager: hostsManager, key: key)
|
ForEach(container.sessionIDs, id: \.self) { key in
|
||||||
|
SessionView(hostsManager: hostsManager, key: key)
|
||||||
|
.id(container.sessions[key]!.handler.connected)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ struct ShellView: View {
|
|||||||
@ObservedObject var handler: SSHHandler
|
@ObservedObject var handler: SSHHandler
|
||||||
@ObservedObject var hostsManager: HostsManager
|
@ObservedObject var hostsManager: HostsManager
|
||||||
|
|
||||||
|
@ObservedObject var container = TerminalViewContainer.shared
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -19,7 +21,7 @@ struct ShellView: View {
|
|||||||
TerminalController(handler: handler, hostsManager: hostsManager)
|
TerminalController(handler: handler, hostsManager: hostsManager)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if let sessionID = handler.sessionID {
|
if let sessionID = handler.sessionID {
|
||||||
TerminalViewContainer.shared[sessionID]?.terminalView.restoreScrollback()
|
container.sessions[sessionID]?.terminalView.restoreScrollback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ struct TerminalController: UIViewRepresentable {
|
|||||||
@ObservedObject var handler: SSHHandler
|
@ObservedObject var handler: SSHHandler
|
||||||
@ObservedObject var hostsManager: HostsManager
|
@ObservedObject var hostsManager: HostsManager
|
||||||
|
|
||||||
|
@ObservedObject var container = TerminalViewContainer.shared
|
||||||
|
|
||||||
func makeUIView(context: Context) -> TerminalView {
|
func makeUIView(context: Context) -> TerminalView {
|
||||||
if let sessionID = handler.sessionID {
|
if let sessionID = handler.sessionID {
|
||||||
if let existing = TerminalViewContainer.shared[sessionID] {
|
if let existing = container.sessions[sessionID] {
|
||||||
return existing.terminalView
|
return existing.terminalView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +32,7 @@ struct TerminalController: UIViewRepresentable {
|
|||||||
tv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
tv.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
|
||||||
if let sessionID = handler.sessionID {
|
if let sessionID = handler.sessionID {
|
||||||
TerminalViewContainer.shared[sessionID] = TerminalContainer(
|
container.sessions[sessionID] = TerminalContainer(
|
||||||
handler: handler,
|
handler: handler,
|
||||||
terminalView: tv
|
terminalView: tv
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,10 +7,15 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public final class TerminalViewContainer {
|
@MainActor
|
||||||
@MainActor static var shared: [
|
public final class TerminalViewContainer: ObservableObject {
|
||||||
UUID: TerminalContainer
|
static let shared = TerminalViewContainer()
|
||||||
] = [:]
|
|
||||||
|
@Published var sessions: [UUID: TerminalContainer] = [:]
|
||||||
|
|
||||||
|
var sessionIDs: [UUID] {
|
||||||
|
return sessions.map({ $0.key })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TerminalContainer {
|
struct TerminalContainer {
|
||||||
|
|||||||
Reference in New Issue
Block a user