mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 05:19:13 +00:00
added history view
added history loading and saving functions added a thingy that will combine multiple entries like the phone app added history data struct fix hosts with no name just bneing called " copy" when duplicated remove the showterminal button fix crash when closing the terminal
This commit is contained in:
@@ -83,6 +83,8 @@
|
||||
A9C060ED2E3FBCD000CA9374 /* SnippetPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C060EC2E3FBCD000CA9374 /* SnippetPicker.swift */; };
|
||||
A9C4140C2E096DB7005E3047 /* SSHError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C4140B2E096DB7005E3047 /* SSHError.swift */; };
|
||||
A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */; };
|
||||
A9CC786B2E4E681400FAEE58 /* RecentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CC786A2E4E681400FAEE58 /* RecentsView.swift */; };
|
||||
A9CC786D2E4F534600FAEE58 /* History.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CC786C2E4F534600FAEE58 /* History.swift */; };
|
||||
A9D819292E0E904200442D38 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D819282E0E904200442D38 /* Theme.swift */; };
|
||||
A9D8192D2E0E9EB500442D38 /* ThemeManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192C2E0E9EB500442D38 /* ThemeManagerView.swift */; };
|
||||
A9D8192F2E0F1BEE00442D38 /* ThemeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192E2E0F1BEE00442D38 /* ThemeButton.swift */; };
|
||||
@@ -214,6 +216,8 @@
|
||||
A9C060EC2E3FBCD000CA9374 /* SnippetPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnippetPicker.swift; sourceTree = "<group>"; };
|
||||
A9C4140B2E096DB7005E3047 /* SSHError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHError.swift; sourceTree = "<group>"; };
|
||||
A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHHandler.swift; sourceTree = "<group>"; };
|
||||
A9CC786A2E4E681400FAEE58 /* RecentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentsView.swift; sourceTree = "<group>"; };
|
||||
A9CC786C2E4F534600FAEE58 /* History.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = History.swift; sourceTree = "<group>"; };
|
||||
A9D819282E0E904200442D38 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
A9D8192C2E0E9EB500442D38 /* ThemeManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManagerView.swift; sourceTree = "<group>"; };
|
||||
A9D8192E2E0F1BEE00442D38 /* ThemeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeButton.swift; sourceTree = "<group>"; };
|
||||
@@ -521,6 +525,7 @@
|
||||
A98554622E0587DF009051BD /* HostsView.swift */,
|
||||
A985545C2E055D4D009051BD /* ConnectionView.swift */,
|
||||
A98CAB432E4229F7005E4C42 /* HostSymbolPicker.swift */,
|
||||
A9CC786A2E4E681400FAEE58 /* RecentsView.swift */,
|
||||
);
|
||||
path = Hosts;
|
||||
sourceTree = "<group>";
|
||||
@@ -542,6 +547,7 @@
|
||||
A93143BF2DF61B3200FCD5DB /* Host.swift */,
|
||||
A98554602E058433009051BD /* HostsManager.swift */,
|
||||
A9DA97702E0D30ED00142DDC /* HostSymbol.swift */,
|
||||
A9CC786C2E4F534600FAEE58 /* History.swift */,
|
||||
);
|
||||
path = Host;
|
||||
sourceTree = "<group>";
|
||||
@@ -792,6 +798,8 @@
|
||||
A96C6A8A2E0C0B1100F377FE /* SSHState.swift in Sources */,
|
||||
A9FD37692E16A6BF005319A8 /* ShellTabView.swift in Sources */,
|
||||
A9DA97732E0D40C100142DDC /* HostSymbolPreview.swift in Sources */,
|
||||
A9CC786B2E4E681400FAEE58 /* RecentsView.swift in Sources */,
|
||||
A9CC786D2E4F534600FAEE58 /* History.swift in Sources */,
|
||||
A90B38342E3EA046002B56FC /* Bundle.swift in Sources */,
|
||||
A9FD376B2E16DABF005319A8 /* AnsiPickerView.swift in Sources */,
|
||||
A9C060ED2E3FBCD000CA9374 /* SnippetPicker.swift in Sources */,
|
||||
|
||||
15
ShhShell/Host/History.swift
Normal file
15
ShhShell/Host/History.swift
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// History.swift
|
||||
// ShhShell
|
||||
//
|
||||
// Created by neon443 on 15/08/2025.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct History: Identifiable {
|
||||
var id: UUID = UUID()
|
||||
|
||||
var host: Host
|
||||
var count: Int
|
||||
}
|
||||
@@ -24,6 +24,8 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
||||
|
||||
@Published var snippets: [Snippet] = []
|
||||
|
||||
@Published var history: [Host] = []
|
||||
|
||||
var tint: SwiftUI.Color {
|
||||
selectedTheme.ansi[selectedAnsi].suiColor
|
||||
}
|
||||
@@ -33,6 +35,37 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
||||
loadThemes()
|
||||
loadFonts()
|
||||
loadSnippets()
|
||||
loadHistory()
|
||||
}
|
||||
|
||||
func loadHistory() {
|
||||
guard let data = userDefaults.data(forKey: "history") else { return }
|
||||
guard let decoded = try? JSONDecoder().decode([Host].self, from: data) else { return }
|
||||
withAnimation { self.history = decoded }
|
||||
}
|
||||
|
||||
func formatHistory() -> [History] {
|
||||
var result: [History] = []
|
||||
for host in history {
|
||||
if result.last?.host == host {
|
||||
guard var lastOne = result.popLast() else { return result }
|
||||
lastOne.count += 1
|
||||
result.append(lastOne)
|
||||
} else {
|
||||
result.append(History(host: host, count: 1))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func saveHistory() {
|
||||
let data = try? JSONEncoder().encode(history)
|
||||
userDefaults.set(data, forKey: "history")
|
||||
}
|
||||
|
||||
func removeFromHistory(_ toRemove: Host) {
|
||||
history.removeAll(where: { $0.id == toRemove.id })
|
||||
saveHistory()
|
||||
}
|
||||
|
||||
func addSnippet(_ toAdd: Snippet) {
|
||||
@@ -239,7 +272,7 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
||||
func duplicateHost(_ hostToDup: Host) {
|
||||
var hostNewID = hostToDup
|
||||
hostNewID.id = UUID()
|
||||
hostNewID.name.append(" copy")
|
||||
hostNewID.name = hostToDup.description.appending(" copy")
|
||||
if let index = hosts.firstIndex(where: { $0 == hostToDup }) {
|
||||
hosts.insert(hostNewID, at: index+1)
|
||||
Haptic.medium.trigger()
|
||||
|
||||
@@ -24,6 +24,11 @@ struct ContentView: View {
|
||||
keyManager: keyManager
|
||||
)
|
||||
|
||||
RecentsView(
|
||||
hostsManager: hostsManager,
|
||||
keyManager: keyManager
|
||||
)
|
||||
|
||||
HostsView(
|
||||
handler: handler,
|
||||
hostsManager: hostsManager,
|
||||
|
||||
@@ -81,13 +81,6 @@ struct ConnectionView: View {
|
||||
}
|
||||
}
|
||||
|
||||
Button() {
|
||||
showTerminal.toggle()
|
||||
} label: {
|
||||
Label("Show Terminal", systemImage: "apple.terminal")
|
||||
}
|
||||
.disabled(!checkShell(handler.state))
|
||||
|
||||
Button() {
|
||||
handler.testExec()
|
||||
} label: {
|
||||
@@ -153,6 +146,7 @@ Hostkey fingerprint is \(handler.getHostkey() ?? "nil")
|
||||
handler.go()
|
||||
showTerminal = checkShell(handler.state)
|
||||
if showTerminal {
|
||||
hostsManager.history.append(handler.host)
|
||||
handler.writeToChannel(hostsManager.snippets.first(where: { $0.id == handler.host.startupSnippetID })?.content)
|
||||
}
|
||||
} label: {
|
||||
|
||||
48
ShhShell/Views/Hosts/RecentsView.swift
Normal file
48
ShhShell/Views/Hosts/RecentsView.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// RecentsView.swift
|
||||
// ShhShell
|
||||
//
|
||||
// Created by neon443 on 14/08/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct RecentsView: View {
|
||||
@ObservedObject var hostsManager: HostsManager
|
||||
@ObservedObject var keyManager: KeyManager
|
||||
|
||||
var body: some View {
|
||||
if !hostsManager.history.isEmpty {
|
||||
Section("Recents") {
|
||||
ForEach(hostsManager.formatHistory()) { history in
|
||||
NavigationLink() {
|
||||
ConnectionView(
|
||||
handler: SSHHandler(
|
||||
host: history.host,
|
||||
keyManager: keyManager
|
||||
),
|
||||
hostsManager: hostsManager,
|
||||
keyManager: keyManager
|
||||
)
|
||||
} label: {
|
||||
Text(history.host.description)
|
||||
Text("\(history.count)")
|
||||
}
|
||||
.swipeActions {
|
||||
Button("Remove", systemImage: "trash", role: .destructive) {
|
||||
hostsManager.removeFromHistory(history.host)
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
RecentsView(
|
||||
hostsManager: HostsManager(),
|
||||
keyManager: KeyManager()
|
||||
)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ struct ShellTabView: View {
|
||||
@ObservedObject var container = TerminalViewContainer.shared
|
||||
@State var selectedID: UUID?
|
||||
var selectedHandler: SSHHandler {
|
||||
container.sessions[selectedID ?? UUID()]?.handler ?? handler!
|
||||
container.sessions[selectedID ?? UUID()]?.handler ?? handler ?? SSHHandler(host: Host.blank, keyManager: nil)
|
||||
}
|
||||
|
||||
@State var showSnippetPicker: Bool = false
|
||||
|
||||
Reference in New Issue
Block a user