mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +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 */; };
|
A9C060ED2E3FBCD000CA9374 /* SnippetPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C060EC2E3FBCD000CA9374 /* SnippetPicker.swift */; };
|
||||||
A9C4140C2E096DB7005E3047 /* SSHError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C4140B2E096DB7005E3047 /* SSHError.swift */; };
|
A9C4140C2E096DB7005E3047 /* SSHError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C4140B2E096DB7005E3047 /* SSHError.swift */; };
|
||||||
A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C897EE2DF1A9A400EF9A5F /* SSHHandler.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 */; };
|
A9D819292E0E904200442D38 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D819282E0E904200442D38 /* Theme.swift */; };
|
||||||
A9D8192D2E0E9EB500442D38 /* ThemeManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192C2E0E9EB500442D38 /* ThemeManagerView.swift */; };
|
A9D8192D2E0E9EB500442D38 /* ThemeManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192C2E0E9EB500442D38 /* ThemeManagerView.swift */; };
|
||||||
A9D8192F2E0F1BEE00442D38 /* ThemeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D8192E2E0F1BEE00442D38 /* ThemeButton.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
A9D8192E2E0F1BEE00442D38 /* ThemeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeButton.swift; sourceTree = "<group>"; };
|
||||||
@@ -521,6 +525,7 @@
|
|||||||
A98554622E0587DF009051BD /* HostsView.swift */,
|
A98554622E0587DF009051BD /* HostsView.swift */,
|
||||||
A985545C2E055D4D009051BD /* ConnectionView.swift */,
|
A985545C2E055D4D009051BD /* ConnectionView.swift */,
|
||||||
A98CAB432E4229F7005E4C42 /* HostSymbolPicker.swift */,
|
A98CAB432E4229F7005E4C42 /* HostSymbolPicker.swift */,
|
||||||
|
A9CC786A2E4E681400FAEE58 /* RecentsView.swift */,
|
||||||
);
|
);
|
||||||
path = Hosts;
|
path = Hosts;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -542,6 +547,7 @@
|
|||||||
A93143BF2DF61B3200FCD5DB /* Host.swift */,
|
A93143BF2DF61B3200FCD5DB /* Host.swift */,
|
||||||
A98554602E058433009051BD /* HostsManager.swift */,
|
A98554602E058433009051BD /* HostsManager.swift */,
|
||||||
A9DA97702E0D30ED00142DDC /* HostSymbol.swift */,
|
A9DA97702E0D30ED00142DDC /* HostSymbol.swift */,
|
||||||
|
A9CC786C2E4F534600FAEE58 /* History.swift */,
|
||||||
);
|
);
|
||||||
path = Host;
|
path = Host;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -792,6 +798,8 @@
|
|||||||
A96C6A8A2E0C0B1100F377FE /* SSHState.swift in Sources */,
|
A96C6A8A2E0C0B1100F377FE /* SSHState.swift in Sources */,
|
||||||
A9FD37692E16A6BF005319A8 /* ShellTabView.swift in Sources */,
|
A9FD37692E16A6BF005319A8 /* ShellTabView.swift in Sources */,
|
||||||
A9DA97732E0D40C100142DDC /* HostSymbolPreview.swift in Sources */,
|
A9DA97732E0D40C100142DDC /* HostSymbolPreview.swift in Sources */,
|
||||||
|
A9CC786B2E4E681400FAEE58 /* RecentsView.swift in Sources */,
|
||||||
|
A9CC786D2E4F534600FAEE58 /* History.swift in Sources */,
|
||||||
A90B38342E3EA046002B56FC /* Bundle.swift in Sources */,
|
A90B38342E3EA046002B56FC /* Bundle.swift in Sources */,
|
||||||
A9FD376B2E16DABF005319A8 /* AnsiPickerView.swift in Sources */,
|
A9FD376B2E16DABF005319A8 /* AnsiPickerView.swift in Sources */,
|
||||||
A9C060ED2E3FBCD000CA9374 /* SnippetPicker.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 snippets: [Snippet] = []
|
||||||
|
|
||||||
|
@Published var history: [Host] = []
|
||||||
|
|
||||||
var tint: SwiftUI.Color {
|
var tint: SwiftUI.Color {
|
||||||
selectedTheme.ansi[selectedAnsi].suiColor
|
selectedTheme.ansi[selectedAnsi].suiColor
|
||||||
}
|
}
|
||||||
@@ -33,6 +35,37 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
loadThemes()
|
loadThemes()
|
||||||
loadFonts()
|
loadFonts()
|
||||||
loadSnippets()
|
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) {
|
func addSnippet(_ toAdd: Snippet) {
|
||||||
@@ -239,7 +272,7 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
func duplicateHost(_ hostToDup: Host) {
|
func duplicateHost(_ hostToDup: Host) {
|
||||||
var hostNewID = hostToDup
|
var hostNewID = hostToDup
|
||||||
hostNewID.id = UUID()
|
hostNewID.id = UUID()
|
||||||
hostNewID.name.append(" copy")
|
hostNewID.name = hostToDup.description.appending(" copy")
|
||||||
if let index = hosts.firstIndex(where: { $0 == hostToDup }) {
|
if let index = hosts.firstIndex(where: { $0 == hostToDup }) {
|
||||||
hosts.insert(hostNewID, at: index+1)
|
hosts.insert(hostNewID, at: index+1)
|
||||||
Haptic.medium.trigger()
|
Haptic.medium.trigger()
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ struct ContentView: View {
|
|||||||
keyManager: keyManager
|
keyManager: keyManager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RecentsView(
|
||||||
|
hostsManager: hostsManager,
|
||||||
|
keyManager: keyManager
|
||||||
|
)
|
||||||
|
|
||||||
HostsView(
|
HostsView(
|
||||||
handler: handler,
|
handler: handler,
|
||||||
hostsManager: hostsManager,
|
hostsManager: hostsManager,
|
||||||
|
|||||||
@@ -81,13 +81,6 @@ struct ConnectionView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button() {
|
|
||||||
showTerminal.toggle()
|
|
||||||
} label: {
|
|
||||||
Label("Show Terminal", systemImage: "apple.terminal")
|
|
||||||
}
|
|
||||||
.disabled(!checkShell(handler.state))
|
|
||||||
|
|
||||||
Button() {
|
Button() {
|
||||||
handler.testExec()
|
handler.testExec()
|
||||||
} label: {
|
} label: {
|
||||||
@@ -153,6 +146,7 @@ Hostkey fingerprint is \(handler.getHostkey() ?? "nil")
|
|||||||
handler.go()
|
handler.go()
|
||||||
showTerminal = checkShell(handler.state)
|
showTerminal = checkShell(handler.state)
|
||||||
if showTerminal {
|
if showTerminal {
|
||||||
|
hostsManager.history.append(handler.host)
|
||||||
handler.writeToChannel(hostsManager.snippets.first(where: { $0.id == handler.host.startupSnippetID })?.content)
|
handler.writeToChannel(hostsManager.snippets.first(where: { $0.id == handler.host.startupSnippetID })?.content)
|
||||||
}
|
}
|
||||||
} label: {
|
} 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
|
@ObservedObject var container = TerminalViewContainer.shared
|
||||||
@State var selectedID: UUID?
|
@State var selectedID: UUID?
|
||||||
var selectedHandler: SSHHandler {
|
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
|
@State var showSnippetPicker: Bool = false
|
||||||
|
|||||||
Reference in New Issue
Block a user