From 288b37a0d6dd817acebd9ad8e1ed170bc0d51783 Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:31:49 +0100 Subject: [PATCH] added a thingy to select an ansi color (accent color) in thememanger improved ui for tab selector, uses theme colours and close button is in the tab made sshhandler optional for shelltabview implemented the tabbed terminal to sessionlistview in home added selectedAnsi to select an accent color from the 16 ansi colors remove toolbar and navbar in shellview, fix the wierd input issue --- ShhShell/Host/HostsManager.swift | 28 ++-- ShhShell/ShhShellApp.swift | 1 + ShhShell/Views/Sessions/SessionView.swift | 5 +- ShhShell/Views/Terminal/ShellTabView.swift | 47 ++++++- ShhShell/Views/Terminal/ShellView.swift | 18 --- ShhShell/Views/Themes/ThemeManagerView.swift | 139 +++++++++++-------- 6 files changed, 141 insertions(+), 97 deletions(-) diff --git a/ShhShell/Host/HostsManager.swift b/ShhShell/Host/HostsManager.swift index 1edff23..91b9320 100644 --- a/ShhShell/Host/HostsManager.swift +++ b/ShhShell/Host/HostsManager.swift @@ -15,6 +15,7 @@ class HostsManager: ObservableObject, @unchecked Sendable { @Published var hosts: [Host] = [] @Published var themes: [Theme] = [] @Published var selectedTheme: Theme = Theme.defaultTheme + @Published var selectedAnsi: Int = 1 init() { loadHosts() @@ -33,11 +34,25 @@ class HostsManager: ObservableObject, @unchecked Sendable { self.themes.append(synthedTheme) } - guard let dataSelTheme = userDefaults.data(forKey: "selectedTheme") else { return } guard let decodedSelTheme = Theme.decodeTheme(data: dataSelTheme) else { return } //name doesnt matter self.selectedTheme = decodedSelTheme + + selectedAnsi = Int(userDefaults.longLong(forKey: "selectedAnsi")) + } + + func saveThemes() { + let encoder = JSONEncoder() + // map the theme to themecodable + guard let encodedThemes = try? encoder.encode(themes.map({$0.themeCodable})) else { return } + userDefaults.set(encodedThemes, forKey: "themes") + + guard let encodedSelectedTheme = try? encoder.encode(selectedTheme.themeCodable) else { return } + userDefaults.set(encodedSelectedTheme, forKey: "selectedTheme") + + userDefaults.set(Int64(selectedAnsi), forKey: "selectedAnsi") + userDefaults.synchronize() } func downloadTheme(fromUrl: URL?) { @@ -87,17 +102,6 @@ class HostsManager: ObservableObject, @unchecked Sendable { saveThemes() } - func saveThemes() { - let encoder = JSONEncoder() - // map the theme to themecodable - guard let encodedThemes = try? encoder.encode(themes.map({$0.themeCodable})) else { return } - userDefaults.set(encodedThemes, forKey: "themes") - - guard let encodedSelectedTheme = try? encoder.encode(selectedTheme.themeCodable) else { return } - userDefaults.set(encodedSelectedTheme, forKey: "selectedTheme") - userDefaults.synchronize() - } - func getHostIndexMatching(_ hostSearchingFor: Host) -> Int? { if let index = hosts.firstIndex(where: { $0.id == hostSearchingFor.id }) { return index diff --git a/ShhShell/ShhShellApp.swift b/ShhShell/ShhShellApp.swift index cb050db..58b279d 100644 --- a/ShhShell/ShhShellApp.swift +++ b/ShhShell/ShhShellApp.swift @@ -28,6 +28,7 @@ struct ShhShellApp: App { keyManager: keyManager ) .colorScheme(.dark) + .tint(hostsManager.selectedTheme.ansi[hostsManager.selectedAnsi].suiColor) } } } diff --git a/ShhShell/Views/Sessions/SessionView.swift b/ShhShell/Views/Sessions/SessionView.swift index 7cb0f15..68fab5a 100644 --- a/ShhShell/Views/Sessions/SessionView.swift +++ b/ShhShell/Views/Sessions/SessionView.swift @@ -34,10 +34,7 @@ struct SessionView: View { } } .fullScreenCover(isPresented: $shellPresented) { - ShellView( - handler: container.sessions[key]?.handler ?? SSHHandler(host: Host.blank, keyManager: keyManager), - hostsManager: hostsManager - ) + ShellTabView(handler: nil, hostsManager: hostsManager, selectedID: key) } } } diff --git a/ShhShell/Views/Terminal/ShellTabView.swift b/ShhShell/Views/Terminal/ShellTabView.swift index c84a6f8..2ab5535 100644 --- a/ShhShell/Views/Terminal/ShellTabView.swift +++ b/ShhShell/Views/Terminal/ShellTabView.swift @@ -8,7 +8,7 @@ import SwiftUI struct ShellTabView: View { - @ObservedObject var handler: SSHHandler + @State var handler: SSHHandler? @ObservedObject var hostsManager: HostsManager @ObservedObject var container = TerminalViewContainer.shared @@ -23,13 +23,38 @@ struct ShellTabView: View { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 0) { ForEach(container.sessionIDs, id: \.self) { id in + let selected = selectedID == id + let foreground = hostsManager.selectedTheme.foreground.suiColor + let ansi7 = hostsManager.selectedTheme.ansi[6].suiColor.opacity(0.7) + let background = hostsManager.selectedTheme.background.suiColor ZStack { Rectangle() - .fill(selectedID == id ? .orange : .gray) - .opacity(0.5) - Text(container.sessions[id]!.handler.host.description) - .frame(width: oneTabWidth) + .fill(selected ? ansi7 : background) + HStack { + if selected { + Button() { + container.sessions[id]?.handler.disconnect() + } label: { + Image(systemName: "xmark.app.fill") + .resizable().scaledToFit() + } + .padding() + } + Spacer() + VStack { + Text(container.sessions[id]!.handler.title) + .monospaced() + .foregroundStyle(foreground) + .bold(selected) + Text(container.sessions[id]!.handler.host.description) + .foregroundStyle(foreground.opacity(0.7)) + .monospaced() + .font(.caption) + } + Spacer() + } } + .frame(width: oneTabWidth) .ignoresSafeArea(.all) .onTapGesture { withAnimation { selectedID = id } @@ -40,7 +65,11 @@ struct ShellTabView: View { .frame(height: 30) .onAppear { if selectedID == nil { - selectedID = handler.sessionID + if let handler { + selectedID = handler.sessionID + } else { + dismiss() + } } } @@ -62,7 +91,11 @@ struct ShellTabView: View { .id(selectedID) .transition(.opacity) } else { - ShellView(handler: handler, hostsManager: hostsManager) + if let handler { + ShellView(handler: handler, hostsManager: hostsManager) + } else { + Text("No SSH Handler") + } } } } diff --git a/ShhShell/Views/Terminal/ShellView.swift b/ShhShell/Views/Terminal/ShellView.swift index b97fbae..a5ac5db 100644 --- a/ShhShell/Views/Terminal/ShellView.swift +++ b/ShhShell/Views/Terminal/ShellView.swift @@ -37,24 +37,6 @@ struct ShellView: View { .onAppear { handler.applySelectedTheme() } - .navigationTitle(handler.title) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button() { - handler.disconnect() - } label: { - Label("Disconnect", systemImage: "xmark.app.fill") - } - } - ToolbarItem(placement: .cancellationAction) { - Button() { - dismiss() - } label: { - Label("Close", systemImage: "arrow.down.right.and.arrow.up.left") - } - } - } } } } diff --git a/ShhShell/Views/Themes/ThemeManagerView.swift b/ShhShell/Views/Themes/ThemeManagerView.swift index de741e2..1528dc3 100644 --- a/ShhShell/Views/Themes/ThemeManagerView.swift +++ b/ShhShell/Views/Themes/ThemeManagerView.swift @@ -33,74 +33,101 @@ struct ThemeManagerView: View { hostsManager.selectedTheme.background.suiColor.opacity(0.7) .ignoresSafeArea(.all) GeometryReader { geo in - let columns: Int = max(1, Int((geo.size.width - 2*spacing) / (minColWidth + spacing))) - let layout = Array(repeating: grid, count: columns) - ScrollView { - if hostsManager.themes.isEmpty { - VStack(alignment: .leading) { - Image(systemName: "paintpalette") - .resizable().scaledToFit() - .symbolRenderingMode(.multicolor) - .frame(width: 50) - Text("No themes (yet)") - .font(.title) - .padding(.vertical, 10) - .bold() - Text("Tap the Safari icon at the top right to find themes!") - Text("Once you find one that you like, copy it's link and enter it here using the link button.") + VStack { + VStack(spacing: 0) { + HStack(spacing: 0) { + ForEach(0..<8, id: \.self) { index in + Rectangle() + .fill(hostsManager.selectedTheme.ansi[index].suiColor) + .onTapGesture { + hostsManager.selectedAnsi = index + hostsManager.saveThemes() + } + } + } + HStack(spacing: 0) { + ForEach(8..<16, id: \.self) { index in + Rectangle() + .fill(hostsManager.selectedTheme.ansi[index].suiColor) + .onTapGesture { + hostsManager.selectedAnsi = index + hostsManager.saveThemes() + } + } + } + } + .clipShape(RoundedRectangle(cornerRadius: 15)) + .frame(maxWidth: geo.size.width, maxHeight: geo.size.height/2) + + let columns: Int = max(1, Int((geo.size.width - 2*spacing) / (minColWidth + spacing))) + let layout = Array(repeating: grid, count: columns) + ScrollView { + if hostsManager.themes.isEmpty { + VStack(alignment: .leading) { + Image(systemName: "paintpalette") + .resizable().scaledToFit() + .symbolRenderingMode(.multicolor) + .frame(width: 50) + Text("No themes (yet)") + .font(.title) + .padding(.vertical, 10) + .bold() + Text("Tap the Safari icon at the top right to find themes!") + Text("Once you find one that you like, copy it's link and enter it here using the link button.") + } + } else { + LazyVGrid(columns: layout, alignment: .center, spacing: 8) { + ForEach(hostsManager.themes) { theme in + ThemePreview(hostsManager: hostsManager, theme: theme, canModify: true) + } + } + .padding(.horizontal) + .animation(.default, value: hostsManager.themes) + } + + HStack { + Text("Built-in Themes") + .padding(.top) + .padding(.horizontal) + .font(.headline) + Spacer() } - } else { LazyVGrid(columns: layout, alignment: .center, spacing: 8) { - ForEach(hostsManager.themes) { theme in - ThemePreview(hostsManager: hostsManager, theme: theme, canModify: true) + ForEach(Theme.builtinThemes) { theme in + ThemePreview(hostsManager: hostsManager, theme: theme, canModify: false) } } .padding(.horizontal) .animation(.default, value: hostsManager.themes) } - - HStack { - Text("Built-in Themes") - .padding(.top) - .padding(.horizontal) - .font(.headline) - Spacer() - } - LazyVGrid(columns: layout, alignment: .center, spacing: 8) { - ForEach(Theme.builtinThemes) { theme in - ThemePreview(hostsManager: hostsManager, theme: theme, canModify: false) - } - } - .padding(.horizontal) - .animation(.default, value: hostsManager.themes) - } - .navigationTitle("Themes") - .alert("Enter URL", isPresented: $showAlert) { - TextField("", text: $importURL, prompt: Text("URL")) - Button("Cancel") {} - Button() { - hostsManager.downloadTheme(fromUrl: URL(string: importURL)) - importURL = "" - } label: { - Label("Import", systemImage: "square.and.arrow.down") - .bold() - } - } - .toolbar { - ToolbarItem() { + .navigationTitle("Themes") + .alert("Enter URL", isPresented: $showAlert) { + TextField("", text: $importURL, prompt: Text("URL")) + Button("Cancel") {} Button() { - UIApplication.shared.open(URL(string: "https://iterm2colorschemes.com")!) + hostsManager.downloadTheme(fromUrl: URL(string: importURL)) + importURL = "" } label: { - Label("Open themes site", systemImage: "safari") + Label("Import", systemImage: "square.and.arrow.down") + .bold() } } - ToolbarItem() { - Button() { - showAlert.toggle() - } label: { - Label("From URL", systemImage: "link") + .toolbar { + ToolbarItem() { + Button() { + UIApplication.shared.open(URL(string: "https://iterm2colorschemes.com")!) + } label: { + Label("Open themes site", systemImage: "safari") + } + } + ToolbarItem() { + Button() { + showAlert.toggle() + } label: { + Label("From URL", systemImage: "link") + } + } - } } }