diff --git a/StikJIT/Extensions & Stuff/Color.swift b/StikJIT/Extensions & Stuff/Color.swift index 6393435..d3e53fe 100644 --- a/StikJIT/Extensions & Stuff/Color.swift +++ b/StikJIT/Extensions & Stuff/Color.swift @@ -45,7 +45,45 @@ extension Color { extension Color { static let primaryBackground = Color.black static let cardBackground = Color.white.opacity(0.2) - static let cardBackground2 = Color.blue.opacity(0.8) + + // Instead of a static color, provide a function that gets the current accent color + static func dynamicAccentColor(opacity: Double = 0.8) -> Color { + let colorHex = UserDefaults.standard.string(forKey: "customAccentColor") ?? "" + if colorHex.isEmpty { + return Color.blue.opacity(opacity) + } else { + return (Color(hex: colorHex) ?? .blue).opacity(opacity) + } + } + + // For backward compatibility + static var cardBackground2: Color { + return dynamicAccentColor(opacity: 0.8) + } + static let primaryText = Color.white static let secondaryText = Color.white.opacity(0.7) + + // Determine if a color is dark or light to decide text color + func isDark() -> Bool { + let uiColor = UIColor(self) + var red: CGFloat = 0 + var green: CGFloat = 0 + var blue: CGFloat = 0 + var alpha: CGFloat = 0 + + uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + + // Calculate perceived brightness using the formula for luminance + // 0.299*R + 0.587*G + 0.114*B + let brightness = (0.299 * red) + (0.587 * green) + (0.114 * blue) + + // Return true if the color is dark (brightness < 0.5) + return brightness < 0.5 + } + + // Returns black or white depending on the background brightness + func contrastText() -> Color { + return self.isDark() ? .white : .black + } } diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index cad1bd1..3fdab0d 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -9,6 +9,18 @@ import SwiftUI import Network import UniformTypeIdentifiers +// Add an accent color environment key +struct AccentColorKey: EnvironmentKey { + static let defaultValue: Color = .blue +} + +extension EnvironmentValues { + var accentColor: Color { + get { self[AccentColorKey.self] } + set { self[AccentColorKey.self] = newValue } + } +} + let fileManager = FileManager.default @@ -442,6 +454,15 @@ func startHeartbeatInBackground() { struct LoadingView: View { @State private var animate = false @Environment(\.colorScheme) private var colorScheme + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" + + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } var body: some View { ZStack { @@ -464,8 +485,8 @@ struct LoadingView: View { .trim(from: 0, to: 0.7) .stroke(AngularGradient( gradient: Gradient(colors: [ - colorScheme == .dark ? Color.white.opacity(0.8) : Color.blue.opacity(0.8), - colorScheme == .dark ? Color.white.opacity(0.3) : Color.blue.opacity(0.3) + accentColor.opacity(0.8), + accentColor.opacity(0.3) ]), center: .center ), style: StrokeStyle(lineWidth: 6, lineCap: .round)) @@ -474,9 +495,7 @@ struct LoadingView: View { .animation(Animation.linear(duration: 1.2).repeatForever(autoreverses: false), value: animate) } // Shadow adapts to theme - .shadow(color: colorScheme == .dark ? - .white.opacity(0.5) : - .blue.opacity(0.3), + .shadow(color: accentColor.opacity(0.4), radius: 10, x: 0, y: 0) .onAppear { animate = true diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index bc07008..8e37aa6 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -11,9 +11,11 @@ import UIKit struct ConsoleLogsView: View { @Environment(\.dismiss) private var dismiss @Environment(\.colorScheme) private var colorScheme + @Environment(\.accentColor) private var environmentAccentColor @StateObject private var logManager = LogManager.shared @State private var autoScroll = true @State private var scrollView: ScrollViewProxy? = nil + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" // Alert handling @State private var showingCustomAlert = false @@ -21,6 +23,14 @@ struct ConsoleLogsView: View { @State private var alertTitle = "" @State private var isError = false + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } + var body: some View { NavigationView { ZStack { @@ -131,10 +141,10 @@ struct ConsoleLogsView: View { }) { HStack { Text("Export Logs") - .foregroundColor(.blue) + .foregroundColor(accentColor) Spacer() Image(systemName: "square.and.arrow.down") - .foregroundColor(.gray) + .foregroundColor(accentColor) } .padding(.vertical, 14) .padding(.horizontal, 20) @@ -172,10 +182,10 @@ struct ConsoleLogsView: View { }) { HStack { Text("Copy Logs") - .foregroundColor(.blue) + .foregroundColor(accentColor) Spacer() Image(systemName: "doc.on.doc") - .foregroundColor(.gray) + .foregroundColor(accentColor) } .padding(.vertical, 14) .padding(.horizontal, 20) @@ -209,7 +219,7 @@ struct ConsoleLogsView: View { Text("Settings") .fontWeight(.regular) } - .foregroundColor(.blue) + .foregroundColor(accentColor) } } @@ -218,7 +228,7 @@ struct ConsoleLogsView: View { logManager.clearLogs() }) { Text("Clear") - .foregroundColor(.blue) + .foregroundColor(accentColor) } } } @@ -298,7 +308,7 @@ struct ConsoleLogsView: View { case .error: return .red case .debug: - return .blue + return accentColor case .warning: return .orange } diff --git a/StikJIT/Views/Custom Error View.swift b/StikJIT/Views/Custom Error View.swift index 8ccb07e..d0282ed 100644 --- a/StikJIT/Views/Custom Error View.swift +++ b/StikJIT/Views/Custom Error View.swift @@ -15,6 +15,15 @@ struct CustomErrorView: View { var messageType: MessageType = .error @Environment(\.colorScheme) private var colorScheme + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" + + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } enum MessageType { case error @@ -51,7 +60,7 @@ struct CustomErrorView: View { case .info: Image(systemName: "info.circle.fill") .font(.system(size: 32)) - .foregroundColor(.blue.opacity(0.9)) + .foregroundColor(accentColor.opacity(0.9)) .padding(.top, 8) } @@ -90,7 +99,7 @@ struct CustomErrorView: View { .frame(maxWidth: .infinity) .background( RoundedRectangle(cornerRadius: 10) - .fill(colorScheme == .dark ? Color.white : Color.blue) + .fill(colorScheme == .dark ? Color.white : accentColor) ) } diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index 749f11b..efbb345 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -17,10 +17,10 @@ extension UIDocumentPickerViewController { struct HomeView: View { @AppStorage("username") private var username = "User" - @AppStorage("customBackgroundColor") private var customBackgroundColorHex: String = "" + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("autoQuitAfterEnablingJIT") private var doAutoQuitAfterEnablingJIT = false - @State private var selectedBackgroundColor: Color = Color(hex: UserDefaults.standard.string(forKey: "customBackgroundColor") ?? "") ?? .clear @Environment(\.colorScheme) private var colorScheme + @Environment(\.accentColor) private var environmentAccentColor let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() @AppStorage("bundleID") private var bundleID: String = "" @State private var isProcessing = false @@ -35,14 +35,18 @@ struct HomeView: View { @State private var viewDidAppeared = false @State private var pendingBundleIdToEnableJIT : String? = nil - + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } var body: some View { ZStack { - // Use the background color or default to appearance-based color if not set - (selectedBackgroundColor == .clear ? - (colorScheme == .dark ? Color.black : Color.white) : - selectedBackgroundColor) + // Use system background + Color(colorScheme == .dark ? .black : .white) .edgesIgnoringSafeArea(.all) VStack(spacing: 25) { @@ -88,10 +92,10 @@ struct HomeView: View { } .frame(maxWidth: .infinity) .padding() - .background(Color.blue) - .foregroundColor(.white) + .background(accentColor) + .foregroundColor(accentColor.contrastText()) .cornerRadius(16) - .shadow(color: Color.blue.opacity(0.3), radius: 8, x: 0, y: 4) + .shadow(color: accentColor.opacity(0.3), radius: 8, x: 0, y: 4) } .padding(.horizontal, 20) @@ -268,11 +272,8 @@ struct HomeView: View { } private func refreshBackground() { - if customBackgroundColorHex.isEmpty { - selectedBackgroundColor = colorScheme == .dark ? Color.black : Color.white - } else { - selectedBackgroundColor = Color(hex: customBackgroundColorHex) ?? (colorScheme == .dark ? Color.black : Color.white) - } + // This function is no longer needed for background color + // but we'll keep it empty to avoid breaking anything } private func startJITInBackground(with bundleID: String) { diff --git a/StikJIT/Views/MainTabView.swift b/StikJIT/Views/MainTabView.swift index 51551f7..57b98f4 100644 --- a/StikJIT/Views/MainTabView.swift +++ b/StikJIT/Views/MainTabView.swift @@ -8,6 +8,16 @@ import SwiftUI struct MainTabView: View { + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" + + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } + var body: some View { TabView { HomeView() @@ -19,5 +29,7 @@ struct MainTabView: View { Label("Settings", systemImage: "gearshape.fill") } } + .accentColor(accentColor) + .environment(\.accentColor, accentColor) } } diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 527aa81..5b65cce 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -8,14 +8,14 @@ import UniformTypeIdentifiers struct SettingsView: View { @AppStorage("username") private var username = "User" - @AppStorage("customBackgroundColor") private var customBackgroundColorHex: String = "" + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("selectedAppIcon") private var selectedAppIcon: String = "AppIcon" @AppStorage("autoQuitAfterEnablingJIT") private var doAutoQuitAfterEnablingJIT = false @AppStorage("skipGetTaskAllowCheck") private var doSkipGetTaskAllowCheck = false @State private var isShowingPairingFilePicker = false @Environment(\.colorScheme) private var colorScheme - @State private var selectedBackgroundColor: Color = .clear + @State private var selectedAccentColor: Color = .blue @State private var showIconPopover = false @State private var showPairingFileMessage = false @State private var pairingFileIsValid = false @@ -46,6 +46,14 @@ struct SettingsView: View { "Wynwxst": "https://github.com/Wynwxst.png" ] + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } + var body: some View { ZStack { Color(UIColor.systemBackground) @@ -108,37 +116,33 @@ struct SettingsView: View { // Theme selector with segmented control style VStack(alignment: .leading, spacing: 10) { - Text("Background Color") + Text("Accent Color") .font(.subheadline) .foregroundColor(.secondary) // Clean segmented style picker Picker("Theme Mode", selection: Binding( - get: { customBackgroundColorHex.isEmpty }, + get: { customAccentColorHex.isEmpty }, set: { newValue in if newValue { - // System theme selected - customBackgroundColorHex = "" - selectedBackgroundColor = colorScheme == .dark ? Color.black : Color.white - } else if customBackgroundColorHex.isEmpty { + // System accent color selected + customAccentColorHex = "" + selectedAccentColor = .blue + } else if customAccentColorHex.isEmpty { // Custom color selected - initialize with current theme color - let initialColor = colorScheme == .dark ? Color.black : Color.white - customBackgroundColorHex = initialColor.toHex() ?? "#000000" - selectedBackgroundColor = initialColor - - // Don't open color picker popup automatically anymore - // User will need to press the "Change Color" button + selectedAccentColor = .blue + customAccentColorHex = selectedAccentColor.toHex() ?? "#0000FF" } } )) { - Text("System").tag(true) + Text("Default").tag(true) Text("Custom").tag(false) } .pickerStyle(SegmentedPickerStyle()) .padding(.vertical, 4) // Show selection button and color picker inline when in custom mode - if !customBackgroundColorHex.isEmpty { + if !customAccentColorHex.isEmpty { HStack { // Left side - just text, no color preview Text("Choose color") @@ -148,11 +152,11 @@ struct SettingsView: View { Spacer() // Right side - embedded color picker - ColorPicker("", selection: $selectedBackgroundColor, supportsOpacity: false) + ColorPicker("", selection: $selectedAccentColor, supportsOpacity: false) .labelsHidden() .scaleEffect(0.8) - .onChange(of: selectedBackgroundColor) { newColor in - saveCustomBackgroundColor(newColor) + .onChange(of: selectedAccentColor) { newColor in + saveCustomAccentColor(newColor) } } .padding(.vertical, 8) @@ -160,8 +164,8 @@ struct SettingsView: View { } // Help text - only show for system mode - if customBackgroundColorHex.isEmpty { - Text("Uses light/dark mode system colors") + if customAccentColorHex.isEmpty { + Text("Uses default blue accent color") .font(.caption) .foregroundColor(.secondary) .padding(.top, 4) @@ -171,7 +175,7 @@ struct SettingsView: View { } .padding(.vertical, 20) .padding(.horizontal, 16) - .animation(.easeInOut(duration: 0.2), value: customBackgroundColorHex.isEmpty) + .animation(.easeInOut(duration: 0.2), value: customAccentColorHex.isEmpty) } SettingsCard { @@ -211,8 +215,8 @@ struct SettingsView: View { } .frame(maxWidth: .infinity) .padding(.vertical, 14) - .foregroundColor(.white) - .background(Color.blue) + .foregroundColor(accentColor.contrastText()) + .background(accentColor) .cornerRadius(12) } @@ -464,7 +468,7 @@ struct SettingsView: View { Spacer() Image(systemName: "gamecontroller") .font(.system(size: 14)) - .foregroundColor(.blue) + .foregroundColor(accentColor) .frame(width: 24) // Keep consistent with LinkRow } } @@ -484,7 +488,7 @@ struct SettingsView: View { HStack { Image(systemName: "terminal") .font(.system(size: 18)) - .foregroundColor(.blue) + .foregroundColor(accentColor) Text("System Logs") .fontWeight(.medium) .foregroundColor(.primary) @@ -597,21 +601,21 @@ struct SettingsView: View { } } .onAppear { - loadCustomBackgroundColor() + loadCustomAccentColor() } } - private func loadCustomBackgroundColor() { - if customBackgroundColorHex.isEmpty { - selectedBackgroundColor = colorScheme == .dark ? Color.black : Color.white + private func loadCustomAccentColor() { + if customAccentColorHex.isEmpty { + selectedAccentColor = .blue } else { - selectedBackgroundColor = Color(hex: customBackgroundColorHex) ?? (colorScheme == .dark ? Color.black : Color.white) + selectedAccentColor = Color(hex: customAccentColorHex) ?? .blue } } - private func saveCustomBackgroundColor(_ color: Color) { + private func saveCustomAccentColor(_ color: Color) { // Always save the custom color if we got here (since auto theme mode is disabled) - customBackgroundColorHex = color.toHex() ?? "" + customAccentColorHex = color.toHex() ?? "" } private func changeAppIcon(to iconName: String) { @@ -682,6 +686,15 @@ struct LinkRow: View { var icon: String var title: String var url: String + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" + + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } var body: some View { Button(action: { @@ -695,7 +708,7 @@ struct LinkRow: View { Spacer() Image(systemName: icon) .font(.system(size: 18)) - .foregroundColor(.blue) + .foregroundColor(accentColor) .frame(width: 24) // Added fixed width } } @@ -781,6 +794,15 @@ struct CollaboratorRow: View { var name: String var url: String var imageUrl: String + @AppStorage("customAccentColor") private var customAccentColorHex: String = "" + + private var accentColor: Color { + if customAccentColorHex.isEmpty { + return .blue + } else { + return Color(hex: customAccentColorHex) ?? .blue + } + } var body: some View { Button(action: { @@ -800,7 +822,7 @@ struct CollaboratorRow: View { Image(systemName: "link") .font(.system(size: 16)) - .foregroundColor(.blue) + .foregroundColor(accentColor) } .padding(.vertical, 8) }