mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
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
This commit is contained in:
@@ -15,6 +15,7 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
@Published var hosts: [Host] = []
|
@Published var hosts: [Host] = []
|
||||||
@Published var themes: [Theme] = []
|
@Published var themes: [Theme] = []
|
||||||
@Published var selectedTheme: Theme = Theme.defaultTheme
|
@Published var selectedTheme: Theme = Theme.defaultTheme
|
||||||
|
@Published var selectedAnsi: Int = 1
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
loadHosts()
|
loadHosts()
|
||||||
@@ -33,11 +34,25 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
self.themes.append(synthedTheme)
|
self.themes.append(synthedTheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
guard let dataSelTheme = userDefaults.data(forKey: "selectedTheme") else { return }
|
guard let dataSelTheme = userDefaults.data(forKey: "selectedTheme") else { return }
|
||||||
guard let decodedSelTheme = Theme.decodeTheme(data: dataSelTheme) else { return }
|
guard let decodedSelTheme = Theme.decodeTheme(data: dataSelTheme) else { return }
|
||||||
//name doesnt matter
|
//name doesnt matter
|
||||||
self.selectedTheme = decodedSelTheme
|
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?) {
|
func downloadTheme(fromUrl: URL?) {
|
||||||
@@ -87,17 +102,6 @@ class HostsManager: ObservableObject, @unchecked Sendable {
|
|||||||
saveThemes()
|
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? {
|
func getHostIndexMatching(_ hostSearchingFor: Host) -> Int? {
|
||||||
if let index = hosts.firstIndex(where: { $0.id == hostSearchingFor.id }) {
|
if let index = hosts.firstIndex(where: { $0.id == hostSearchingFor.id }) {
|
||||||
return index
|
return index
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ struct ShhShellApp: App {
|
|||||||
keyManager: keyManager
|
keyManager: keyManager
|
||||||
)
|
)
|
||||||
.colorScheme(.dark)
|
.colorScheme(.dark)
|
||||||
|
.tint(hostsManager.selectedTheme.ansi[hostsManager.selectedAnsi].suiColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ struct SessionView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fullScreenCover(isPresented: $shellPresented) {
|
.fullScreenCover(isPresented: $shellPresented) {
|
||||||
ShellView(
|
ShellTabView(handler: nil, hostsManager: hostsManager, selectedID: key)
|
||||||
handler: container.sessions[key]?.handler ?? SSHHandler(host: Host.blank, keyManager: keyManager),
|
|
||||||
hostsManager: hostsManager
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ShellTabView: View {
|
struct ShellTabView: View {
|
||||||
@ObservedObject var handler: SSHHandler
|
@State var handler: SSHHandler?
|
||||||
@ObservedObject var hostsManager: HostsManager
|
@ObservedObject var hostsManager: HostsManager
|
||||||
|
|
||||||
@ObservedObject var container = TerminalViewContainer.shared
|
@ObservedObject var container = TerminalViewContainer.shared
|
||||||
@@ -23,13 +23,38 @@ struct ShellTabView: View {
|
|||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 0) {
|
||||||
ForEach(container.sessionIDs, id: \.self) { id in
|
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 {
|
ZStack {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(selectedID == id ? .orange : .gray)
|
.fill(selected ? ansi7 : background)
|
||||||
.opacity(0.5)
|
HStack {
|
||||||
Text(container.sessions[id]!.handler.host.description)
|
if selected {
|
||||||
.frame(width: oneTabWidth)
|
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)
|
.ignoresSafeArea(.all)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
withAnimation { selectedID = id }
|
withAnimation { selectedID = id }
|
||||||
@@ -40,7 +65,11 @@ struct ShellTabView: View {
|
|||||||
.frame(height: 30)
|
.frame(height: 30)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if selectedID == nil {
|
if selectedID == nil {
|
||||||
|
if let handler {
|
||||||
selectedID = handler.sessionID
|
selectedID = handler.sessionID
|
||||||
|
} else {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +91,11 @@ struct ShellTabView: View {
|
|||||||
.id(selectedID)
|
.id(selectedID)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
} else {
|
} else {
|
||||||
|
if let handler {
|
||||||
ShellView(handler: handler, hostsManager: hostsManager)
|
ShellView(handler: handler, hostsManager: hostsManager)
|
||||||
|
} else {
|
||||||
|
Text("No SSH Handler")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,24 +37,6 @@ struct ShellView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
handler.applySelectedTheme()
|
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,32 @@ struct ThemeManagerView: View {
|
|||||||
hostsManager.selectedTheme.background.suiColor.opacity(0.7)
|
hostsManager.selectedTheme.background.suiColor.opacity(0.7)
|
||||||
.ignoresSafeArea(.all)
|
.ignoresSafeArea(.all)
|
||||||
GeometryReader { geo in
|
GeometryReader { geo in
|
||||||
|
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 columns: Int = max(1, Int((geo.size.width - 2*spacing) / (minColWidth + spacing)))
|
||||||
let layout = Array(repeating: grid, count: columns)
|
let layout = Array(repeating: grid, count: columns)
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -107,6 +133,7 @@ struct ThemeManagerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
|
|||||||
Reference in New Issue
Block a user