diff --git a/ShhShell.xcodeproj/project.pbxproj b/ShhShell.xcodeproj/project.pbxproj index fd3009e..4c45de6 100644 --- a/ShhShell.xcodeproj/project.pbxproj +++ b/ShhShell.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ A90936B02E1AE9AB00856059 /* MesloLGS NF Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A90936A12E1AE9AB00856059 /* MesloLGS NF Italic.ttf */; }; A90B38322E3E8FC9002B56FC /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90B38312E3E8FC9002B56FC /* AboutView.swift */; }; A90B38342E3EA046002B56FC /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90B38332E3EA046002B56FC /* Bundle.swift */; }; + A91D27C72E54B97E00620B29 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91D27C62E54B97E00620B29 /* SettingsView.swift */; }; + A91D27CA2E54BED300620B29 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91D27C92E54BED300620B29 /* AppSettings.swift */; }; A923172A2E07113100ECE1E6 /* TerminalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92317292E07113100ECE1E6 /* TerminalController.swift */; }; A923172D2E07138000ECE1E6 /* SSHTerminalDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A923172C2E07138000ECE1E6 /* SSHTerminalDelegate.swift */; }; A923172F2E08851200ECE1E6 /* ShellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A923172E2E08851200ECE1E6 /* ShellView.swift */; }; @@ -149,6 +151,8 @@ A90936A52E1AE9AB00856059 /* SF-Mono-RegularItalic.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Mono-RegularItalic.otf"; sourceTree = ""; }; A90B38312E3E8FC9002B56FC /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; A90B38332E3EA046002B56FC /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; + A91D27C62E54B97E00620B29 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + A91D27C92E54BED300620B29 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; A92317292E07113100ECE1E6 /* TerminalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalController.swift; sourceTree = ""; }; A923172C2E07138000ECE1E6 /* SSHTerminalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHTerminalDelegate.swift; sourceTree = ""; }; A923172E2E08851200ECE1E6 /* ShellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ShellView.swift; path = ShhShell/Views/Terminal/ShellView.swift; sourceTree = SOURCE_ROOT; }; @@ -335,6 +339,14 @@ path = About; sourceTree = ""; }; + A91D27C82E54BEC700620B29 /* Settings */ = { + isa = PBXGroup; + children = ( + A91D27C92E54BED300620B29 /* AppSettings.swift */, + ); + path = Settings; + sourceTree = ""; + }; A923172B2E0712F200ECE1E6 /* Terminal */ = { isa = PBXGroup; children = ( @@ -381,6 +393,7 @@ A9D8192A2E0E904900442D38 /* Themes */, A90936862E1AC4C600856059 /* Fonts */, A93F283F2E2A5EC80092B8D5 /* Snippets */, + A91D27C82E54BEC700620B29 /* Settings */, A9C060E92E357FC400CA9374 /* Misc */, A92538D32DEE0749007E0A18 /* Views */, A90936852E1AC33C00856059 /* Info.plist */, @@ -417,6 +430,7 @@ A9D8192B2E0E9EA400442D38 /* Themes */, A9485C742E1AF58C00209824 /* Fonts */, A93F283E2E2A5DDE0092B8D5 /* Snippets */, + A9FFD4ED2E53B032004CC4B8 /* Settings */, A90B38302E3E8FBA002B56FC /* About */, A96C6B032E0C523600F377FE /* Misc */, ); @@ -620,6 +634,14 @@ path = KeychainLayer; sourceTree = ""; }; + A9FFD4ED2E53B032004CC4B8 /* Settings */ = { + isa = PBXGroup; + children = ( + A91D27C62E54B97E00620B29 /* SettingsView.swift */, + ); + path = Settings; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -798,6 +820,7 @@ A9485C782E1BFA5000209824 /* ThemeEditorView.swift in Sources */, A93143C62DF61FE300FCD5DB /* ViewModifiers.swift in Sources */, A98554632E0587DF009051BD /* HostsView.swift in Sources */, + A91D27CA2E54BED300620B29 /* AppSettings.swift in Sources */, A96C6A8A2E0C0B1100F377FE /* SSHState.swift in Sources */, A9FD37692E16A6BF005319A8 /* ShellTabView.swift in Sources */, A9DA97732E0D40C100142DDC /* HostSymbolPreview.swift in Sources */, @@ -806,6 +829,7 @@ A90B38342E3EA046002B56FC /* Bundle.swift in Sources */, A9FD376B2E16DABF005319A8 /* AnsiPickerView.swift in Sources */, A9C060ED2E3FBCD000CA9374 /* SnippetPicker.swift in Sources */, + A91D27C72E54B97E00620B29 /* SettingsView.swift in Sources */, A96BE6A62E113DB000C0FEE9 /* ColorCodable.swift in Sources */, A92538C82DEE0742007E0A18 /* ContentView.swift in Sources */, A9231A572E52041500974753 /* HostPreview.swift in Sources */, diff --git a/ShhShell/Host/HostsManager.swift b/ShhShell/Host/HostsManager.swift index 400a67e..f1774b7 100644 --- a/ShhShell/Host/HostsManager.swift +++ b/ShhShell/Host/HostsManager.swift @@ -32,6 +32,7 @@ class HostsManager: ObservableObject, @unchecked Sendable { init(previews: Bool = false) { loadHosts() + exportHosts() loadThemes() loadFonts() loadSnippets() @@ -306,6 +307,16 @@ class HostsManager: ObservableObject, @unchecked Sendable { } } + func exportHosts() { + guard let encoded = try? JSONEncoder().encode(hosts) else { return } + print(encoded.base64EncodedString()) + } + + func importHosts(_ data: Data) { + guard let decoedd = try? JSONDecoder().decode([Host].self, from: data) else { return } + hosts = decoedd + } + func removeHost(_ host: Host) { if let index = hosts.firstIndex(where: { $0.id == host.id }) { let _ = withAnimation { hosts.remove(at: index) } diff --git a/ShhShell/SSH/SSHHandler.swift b/ShhShell/SSH/SSHHandler.swift index eacda5c..1835afe 100644 --- a/ShhShell/SSH/SSHHandler.swift +++ b/ShhShell/SSH/SSHHandler.swift @@ -381,16 +381,8 @@ class SSHHandler: @unchecked Sendable, ObservableObject { let data = Data(bytes: buffer, count: Int(nbytes)) if let string = String(data: data, encoding: .utf8) { - #if DEBUG -// print(String(data: Data(bytes: buffer, count: Int(nbytes)), encoding: .utf8)!) - #endif Task { @MainActor in scrollback.append(string) -// if scrollbackSize/1024/1024 > 10 { -// scrollback.remove(at: 0) -// } else { -// scrollbackSize += Double(string.lengthOfBytes(using: .utf8)) -// } } return string } @@ -420,19 +412,8 @@ class SSHHandler: @unchecked Sendable, ObservableObject { guard ssh_channel_is_eof(channel) == 0 else { throw .backendError("Channel is EOF") } ssh_channel_change_pty_size(channel, Int32(toCols), Int32(toRows)) -// print("resized tty to \(toRows)rows and \(toCols)cols") } -// func prettyScrollbackSize() -> String { -// if (scrollbackSize/1024/1024) > 1 { -// return "\(scrollbackSize/1024/1024) MiB scrollback" -// } else if scrollbackSize/1024 > 1 { -// return "\(scrollbackSize/1024) KiB scrollback" -// } else { -// return "\(scrollbackSize) B scrollback" -// } -// } - private func logSshGetError() { guard var session = self.session else { return } logger.critical("\(String(cString: ssh_get_error(&session)))") diff --git a/ShhShell/Settings/AppSettings.swift b/ShhShell/Settings/AppSettings.swift new file mode 100644 index 0000000..431ef76 --- /dev/null +++ b/ShhShell/Settings/AppSettings.swift @@ -0,0 +1,34 @@ +// +// AppSettings.swift +// ShhShell +// +// Created by neon443 on 19/08/2025. +// + +import Foundation + +struct AppSettings: Codable, Sendable { + var scrollback: Int = 1_000 + var cursorStyle: CursorStyle = .block + var locationPersist: Bool = false + var bellSound: Bool = false + var bellHaptic: Bool = true + var caffeinate: Bool = false + var filter: TerminalFilter = .none + var appIcon: AppIcon = .regular +} + +enum CursorStyle: Codable { + case block + case bar +} + +enum TerminalFilter: Codable { + case none + case crt +} + +enum AppIcon: Codable { + case regular + case blueprint +} diff --git a/ShhShell/Views/ContentView.swift b/ShhShell/Views/ContentView.swift index f9feea6..526308c 100644 --- a/ShhShell/Views/ContentView.swift +++ b/ShhShell/Views/ContentView.swift @@ -69,6 +69,11 @@ struct ContentView: View { } Section { + NavigationLink { + SettingsView(hostsManager: hostsManager, keyManager: keyManager) + } label: { + Label("Settings", systemImage: "gear") + } NavigationLink { AboutView(hostsManager: hostsManager) } label: { diff --git a/ShhShell/Views/Settings/SettingsView.swift b/ShhShell/Views/Settings/SettingsView.swift new file mode 100644 index 0000000..6bff861 --- /dev/null +++ b/ShhShell/Views/Settings/SettingsView.swift @@ -0,0 +1,38 @@ +// +// SettingsView.swift +// ShhShell +// +// Created by neon443 on 19/08/2025. +// + +import SwiftUI + +struct SettingsView: View { + @ObservedObject var hostsManager: HostsManager + @ObservedObject var keyManager: KeyManager + + var body: some View { + ZStack { + hostsManager.selectedTheme.background.suiColor.opacity(0.7) + .ignoresSafeArea(.all) + List { + Section("Terminal") { + Label("Scrollback", systemImage: "scroll") + HStack { + Slider(value: .constant(0), in: 1_000...50_000, step: 1_000) + Text("\()") + } + } + } + .listStyle(.sidebar) + .scrollContentBackground(.hidden) + } + } +} + +#Preview { + SettingsView( + hostsManager: HostsManager(), + keyManager: KeyManager() + ) +}