diff --git a/MacTorn-v1.4.3.zip b/MacTorn-v1.4.3.zip deleted file mode 100644 index 1bc3c95..0000000 Binary files a/MacTorn-v1.4.3.zip and /dev/null differ diff --git a/MacTorn-v1.4.4.zip b/MacTorn-v1.4.4.zip new file mode 100644 index 0000000..89081fe Binary files /dev/null and b/MacTorn-v1.4.4.zip differ diff --git a/MacTorn/MacTorn.xcodeproj/project.pbxproj b/MacTorn/MacTorn.xcodeproj/project.pbxproj index 566a322..f75a689 100644 --- a/MacTorn/MacTorn.xcodeproj/project.pbxproj +++ b/MacTorn/MacTorn.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ AAA00021 /* NetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10022 /* NetworkSession.swift */; }; AAA00022 /* TravelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10023 /* TravelView.swift */; }; AAA00023 /* CreditsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10024 /* CreditsView.swift */; }; + AAA00024 /* TransparencyEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAA10025 /* TransparencyEnvironment.swift */; }; /* Unit Tests */ BBB00001 /* MockNetworkSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB10001 /* MockNetworkSession.swift */; }; BBB00002 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBB10002 /* TestHelpers.swift */; }; @@ -89,6 +90,7 @@ AAA10022 /* NetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSession.swift; sourceTree = ""; }; AAA10023 /* TravelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TravelView.swift; sourceTree = ""; }; AAA10024 /* CreditsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsView.swift; sourceTree = ""; }; + AAA10025 /* TransparencyEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransparencyEnvironment.swift; sourceTree = ""; }; AAA10000 /* MacTorn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MacTorn.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* Unit Test Files */ BBB10001 /* MockNetworkSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkSession.swift; sourceTree = ""; }; @@ -155,6 +157,7 @@ AAA30005 /* Views */, AAA30007 /* Utilities */, AAA30008 /* Networking */, + AAA30009 /* Helpers */, ); path = MacTorn; sourceTree = ""; @@ -233,6 +236,14 @@ path = Networking; sourceTree = ""; }; + AAA30009 /* Helpers */ = { + isa = PBXGroup; + children = ( + AAA10025 /* TransparencyEnvironment.swift */, + ); + path = Helpers; + sourceTree = ""; + }; /* Unit Tests Groups */ BBB30000 /* MacTornTests */ = { isa = PBXGroup; @@ -445,6 +456,7 @@ AAA00021 /* NetworkSession.swift in Sources */, AAA00022 /* TravelView.swift in Sources */, AAA00023 /* CreditsView.swift in Sources */, + AAA00024 /* TransparencyEnvironment.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -627,7 +639,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.4.3; + MARKETING_VERSION = 1.4.4; PRODUCT_BUNDLE_IDENTIFIER = com.mactorn.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -654,7 +666,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.4.3; + MARKETING_VERSION = 1.4.4; PRODUCT_BUNDLE_IDENTIFIER = com.mactorn.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -672,7 +684,7 @@ DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.4.3; + MARKETING_VERSION = 1.4.4; PRODUCT_BUNDLE_IDENTIFIER = com.mactorn.MacTornTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -690,7 +702,7 @@ DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.4.3; + MARKETING_VERSION = 1.4.4; PRODUCT_BUNDLE_IDENTIFIER = com.mactorn.MacTornTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -708,7 +720,7 @@ DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.4.3; + MARKETING_VERSION = 1.4.4; PRODUCT_BUNDLE_IDENTIFIER = com.mactorn.MacTornUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; @@ -725,7 +737,7 @@ DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.4.3; + MARKETING_VERSION = 1.4.4; PRODUCT_BUNDLE_IDENTIFIER = com.mactorn.MacTornUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; diff --git a/MacTorn/MacTorn/Helpers/TransparencyEnvironment.swift b/MacTorn/MacTorn/Helpers/TransparencyEnvironment.swift new file mode 100644 index 0000000..b06db47 --- /dev/null +++ b/MacTorn/MacTorn/Helpers/TransparencyEnvironment.swift @@ -0,0 +1,13 @@ +import SwiftUI + +// Environment key for reduce transparency setting +private struct ReduceTransparencyKey: EnvironmentKey { + static let defaultValue: Bool = false +} + +extension EnvironmentValues { + var reduceTransparency: Bool { + get { self[ReduceTransparencyKey.self] } + set { self[ReduceTransparencyKey.self] = newValue } + } +} diff --git a/MacTorn/MacTorn/MacTornApp.swift b/MacTorn/MacTorn/MacTornApp.swift index 4ffe9e3..cb49af5 100644 --- a/MacTorn/MacTorn/MacTornApp.swift +++ b/MacTorn/MacTorn/MacTornApp.swift @@ -3,16 +3,37 @@ import SwiftUI @main struct MacTornApp: App { @StateObject private var appState = AppState() + @AppStorage("appearanceMode") private var appearanceModeRaw: String = AppearanceMode.system.rawValue + @AppStorage("reduceTransparency") private var reduceTransparency: Bool = false var body: some Scene { MenuBarExtra { ContentView() .environmentObject(appState) + .environment(\.reduceTransparency, reduceTransparency) + .onAppear { + updateAppearance() + } + .onChange(of: appearanceModeRaw) { _ in + updateAppearance() + } } label: { MenuBarLabel(appState: appState) } .menuBarExtraStyle(.window) } + + private func updateAppearance() { + let mode = AppearanceMode(rawValue: appearanceModeRaw) ?? .system + switch mode { + case .system: + NSApp.appearance = nil + case .light: + NSApp.appearance = NSAppearance(named: .aqua) + case .dark: + NSApp.appearance = NSAppearance(named: .darkAqua) + } + } } // MARK: - Menu Bar Label diff --git a/MacTorn/MacTorn/ViewModels/AppState.swift b/MacTorn/MacTorn/ViewModels/AppState.swift index 138956c..f92af9b 100644 --- a/MacTorn/MacTorn/ViewModels/AppState.swift +++ b/MacTorn/MacTorn/ViewModels/AppState.swift @@ -5,11 +5,27 @@ import os.log private let logger = Logger(subsystem: "com.mactorn", category: "AppState") +// MARK: - Appearance +enum AppearanceMode: String, CaseIterable { + case system = "System" + case light = "Light" + case dark = "Dark" + + var colorScheme: ColorScheme? { + switch self { + case .system: return nil + case .light: return .light + case .dark: return .dark + } + } +} + @MainActor class AppState: ObservableObject { // MARK: - Persisted @AppStorage("apiKey") var apiKey: String = "" @AppStorage("refreshInterval") var refreshInterval: Int = 30 + @AppStorage("appearanceMode") var appearanceMode: String = AppearanceMode.system.rawValue // MARK: - Published State @Published var data: TornResponse? diff --git a/MacTorn/MacTorn/Views/AttacksView.swift b/MacTorn/MacTorn/Views/AttacksView.swift index 00383f1..46ef233 100644 --- a/MacTorn/MacTorn/Views/AttacksView.swift +++ b/MacTorn/MacTorn/Views/AttacksView.swift @@ -2,6 +2,7 @@ import SwiftUI struct AttacksView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency var body: some View { ScrollView { @@ -39,9 +40,9 @@ struct AttacksView: View { } } .padding() - .background(Color.red.opacity(0.05)) + .background(Color.red.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) - + // Recent Attacks VStack(alignment: .leading, spacing: 8) { HStack { @@ -91,9 +92,9 @@ struct AttacksView: View { } } .padding() - .background(Color.orange.opacity(0.05)) + .background(Color.orange.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) - + // Actions HStack(spacing: 8) { ActionButton(title: "Attack", icon: "bolt.fill", color: .red) { @@ -134,10 +135,11 @@ struct AttacksView: View { // MARK: - Stat Item struct StatItem: View { + @Environment(\.reduceTransparency) private var reduceTransparency let label: String let value: String let color: Color - + var body: some View { VStack(spacing: 2) { Text(value) @@ -149,7 +151,7 @@ struct StatItem: View { } .frame(maxWidth: .infinity) .padding(.vertical, 4) - .background(color.opacity(0.1)) + .background(color.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(4) } } diff --git a/MacTorn/MacTorn/Views/Components/ChainView.swift b/MacTorn/MacTorn/Views/Components/ChainView.swift index dc6541d..866b68f 100644 --- a/MacTorn/MacTorn/Views/Components/ChainView.swift +++ b/MacTorn/MacTorn/Views/Components/ChainView.swift @@ -1,6 +1,7 @@ import SwiftUI struct ChainView: View { + @Environment(\.reduceTransparency) private var reduceTransparency let chain: Chain let fetchTime: Date @@ -25,7 +26,7 @@ struct ChainView: View { } } .padding(8) - .background(color.opacity(0.1)) + .background(color.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(8) } } else if chain.isOnCooldown { diff --git a/MacTorn/MacTorn/Views/Components/EventsView.swift b/MacTorn/MacTorn/Views/Components/EventsView.swift index ad9e31d..2d7cb3b 100644 --- a/MacTorn/MacTorn/Views/Components/EventsView.swift +++ b/MacTorn/MacTorn/Views/Components/EventsView.swift @@ -1,6 +1,7 @@ import SwiftUI struct EventsView: View { + @Environment(\.reduceTransparency) private var reduceTransparency let events: [TornEvent] var body: some View { @@ -33,7 +34,7 @@ struct EventsView: View { } } .padding(8) - .background(Color.blue.opacity(0.05)) + .background(Color.blue.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) } diff --git a/MacTorn/MacTorn/Views/Components/ProgressBarView.swift b/MacTorn/MacTorn/Views/Components/ProgressBarView.swift index 06d432a..2bf0bee 100644 --- a/MacTorn/MacTorn/Views/Components/ProgressBarView.swift +++ b/MacTorn/MacTorn/Views/Components/ProgressBarView.swift @@ -1,6 +1,7 @@ import SwiftUI struct ProgressBarView: View { + @Environment(\.reduceTransparency) private var reduceTransparency let label: String let current: Int let maximum: Int @@ -40,10 +41,10 @@ struct ProgressBarView: View { ZStack(alignment: .leading) { // Background track RoundedRectangle(cornerRadius: 4) - .fill(Color.gray.opacity(0.3)) + .fill(Color.gray.opacity(reduceTransparency ? 0.5 : 0.3)) .overlay( RoundedRectangle(cornerRadius: 4) - .stroke(color.opacity(0.3), lineWidth: 1) + .stroke(color.opacity(reduceTransparency ? 0.5 : 0.3), lineWidth: 1) ) // Filled progress @@ -51,13 +52,13 @@ struct ProgressBarView: View { RoundedRectangle(cornerRadius: 4) .fill( LinearGradient( - colors: [color, color.opacity(0.7)], + colors: [color, color.opacity(reduceTransparency ? 0.9 : 0.7)], startPoint: .leading, endPoint: .trailing ) ) .frame(width: max(4, geometry.size.width * progress)) - .shadow(color: color.opacity(0.5), radius: 2, x: 0, y: 0) + .shadow(color: color.opacity(reduceTransparency ? 0.7 : 0.5), radius: 2, x: 0, y: 0) } } } diff --git a/MacTorn/MacTorn/Views/Components/StatusBadgesView.swift b/MacTorn/MacTorn/Views/Components/StatusBadgesView.swift index e6a9590..ff6c1fd 100644 --- a/MacTorn/MacTorn/Views/Components/StatusBadgesView.swift +++ b/MacTorn/MacTorn/Views/Components/StatusBadgesView.swift @@ -1,6 +1,7 @@ import SwiftUI struct StatusBadgesView: View { + @Environment(\.reduceTransparency) private var reduceTransparency let status: Status var body: some View { @@ -18,10 +19,10 @@ struct StatusBadgesView: View { } .padding(.horizontal, 8) .padding(.vertical, 4) - .background(Color.red.opacity(0.1)) + .background(Color.red.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(6) } - + if status.isInJail { HStack(spacing: 4) { Image(systemName: "lock.fill") @@ -34,7 +35,7 @@ struct StatusBadgesView: View { } .padding(.horizontal, 8) .padding(.vertical, 4) - .background(Color.orange.opacity(0.1)) + .background(Color.orange.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(6) } } diff --git a/MacTorn/MacTorn/Views/ContentView.swift b/MacTorn/MacTorn/Views/ContentView.swift index 65c68c3..89df6a3 100644 --- a/MacTorn/MacTorn/Views/ContentView.swift +++ b/MacTorn/MacTorn/Views/ContentView.swift @@ -22,6 +22,7 @@ enum AppTab: String, CaseIterable { struct ContentView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency @State private var showSettings = false @State private var currentTab: AppTab = .status @@ -55,8 +56,8 @@ struct ContentView: View { // Loading Overlay if appState.isLoading && appState.lastUpdated == nil { - Color.black.opacity(0.4) - .background(.ultraThinMaterial) + (reduceTransparency ? Color(.windowBackgroundColor) : Color.black.opacity(0.4)) + .background(reduceTransparency ? AnyShapeStyle(Color(.windowBackgroundColor)) : AnyShapeStyle(.ultraThinMaterial)) VStack(spacing: 12) { ProgressView() @@ -106,7 +107,7 @@ struct ContentView: View { } .frame(maxWidth: .infinity) .padding(.vertical, 6) - .background(currentTab == tab ? Color.accentColor.opacity(0.2) : Color.clear) + .background(currentTab == tab ? Color.accentColor.opacity(reduceTransparency ? 0.3 : 0.2) : Color.clear) .cornerRadius(6) .contentShape(Rectangle()) // Make entire area clickable } diff --git a/MacTorn/MacTorn/Views/CreditsView.swift b/MacTorn/MacTorn/Views/CreditsView.swift index de0d5ad..8f0e6c6 100644 --- a/MacTorn/MacTorn/Views/CreditsView.swift +++ b/MacTorn/MacTorn/Views/CreditsView.swift @@ -1,6 +1,7 @@ import SwiftUI struct CreditsView: View { + @Environment(\.reduceTransparency) private var reduceTransparency @Binding var showCredits: Bool // MARK: - Developer @@ -107,7 +108,7 @@ struct CreditsView: View { .foregroundColor(.accentColor) .padding(10) .frame(maxWidth: .infinity) - .background(Color.orange.opacity(0.1)) + .background(Color.orange.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(8) } } @@ -139,7 +140,7 @@ struct CreditsView: View { .foregroundColor(.accentColor) .padding(10) .frame(maxWidth: .infinity) - .background(Color.secondary.opacity(0.1)) + .background(Color.secondary.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(8) } } @@ -171,7 +172,7 @@ struct CreditsView: View { .foregroundColor(.accentColor) .padding(10) .frame(maxWidth: .infinity) - .background(Color.secondary.opacity(0.1)) + .background(Color.secondary.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(8) } } @@ -199,7 +200,7 @@ struct CreditsView: View { } .padding(10) .frame(maxWidth: .infinity) - .background(Color.secondary.opacity(0.1)) + .background(Color.secondary.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(8) } } diff --git a/MacTorn/MacTorn/Views/FactionView.swift b/MacTorn/MacTorn/Views/FactionView.swift index 1fa44e7..900ab8a 100644 --- a/MacTorn/MacTorn/Views/FactionView.swift +++ b/MacTorn/MacTorn/Views/FactionView.swift @@ -2,6 +2,7 @@ import SwiftUI struct FactionView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency var body: some View { ScrollView { @@ -33,7 +34,7 @@ struct FactionView: View { .foregroundColor(chainColor(faction.chain)) } .padding(8) - .background(chainColor(faction.chain).opacity(0.1)) + .background(chainColor(faction.chain).opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(6) } @@ -58,9 +59,9 @@ struct FactionView: View { } } .padding() - .background(Color.blue.opacity(0.05)) + .background(Color.blue.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) - + // Armory Quick Actions VStack(alignment: .leading, spacing: 8) { HStack { @@ -85,9 +86,9 @@ struct FactionView: View { } } .padding() - .background(Color.purple.opacity(0.05)) + .background(Color.purple.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) - + // Actions HStack(spacing: 8) { ActionButton(title: "Faction", icon: "person.3.fill", color: .blue) { @@ -138,11 +139,12 @@ struct FactionView: View { // MARK: - Armory Button struct ArmoryButton: View { + @Environment(\.reduceTransparency) private var reduceTransparency let title: String let icon: String let color: Color let action: () -> Void - + var body: some View { Button(action: action) { VStack(spacing: 2) { @@ -153,7 +155,7 @@ struct ArmoryButton: View { } .frame(maxWidth: .infinity) .padding(.vertical, 6) - .background(color.opacity(0.15)) + .background(color.opacity(reduceTransparency ? 0.4 : 0.15)) .foregroundColor(color) .cornerRadius(6) } diff --git a/MacTorn/MacTorn/Views/MoneyView.swift b/MacTorn/MacTorn/Views/MoneyView.swift index a089021..cc85d37 100644 --- a/MacTorn/MacTorn/Views/MoneyView.swift +++ b/MacTorn/MacTorn/Views/MoneyView.swift @@ -2,6 +2,7 @@ import SwiftUI struct MoneyView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency var body: some View { ScrollView { @@ -72,9 +73,9 @@ struct MoneyView: View { } } .padding() - .background(Color.green.opacity(0.05)) + .background(Color.green.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) - + // Actions HStack(spacing: 8) { ActionButton(title: "Send Money", icon: "paperplane.fill", color: .blue) { @@ -112,11 +113,12 @@ struct MoneyView: View { // MARK: - Action Button Component struct ActionButton: View { + @Environment(\.reduceTransparency) private var reduceTransparency let title: String let icon: String let color: Color let action: () -> Void - + var body: some View { Button(action: action) { VStack(spacing: 4) { @@ -127,7 +129,7 @@ struct ActionButton: View { } .frame(maxWidth: .infinity) .padding(.vertical, 8) - .background(color.opacity(0.1)) + .background(color.opacity(reduceTransparency ? 0.4 : 0.1)) .foregroundColor(color) .cornerRadius(8) } diff --git a/MacTorn/MacTorn/Views/PropertiesView.swift b/MacTorn/MacTorn/Views/PropertiesView.swift index c38143f..e5004b4 100644 --- a/MacTorn/MacTorn/Views/PropertiesView.swift +++ b/MacTorn/MacTorn/Views/PropertiesView.swift @@ -2,6 +2,7 @@ import SwiftUI struct PropertiesView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency var body: some View { ScrollView { @@ -58,6 +59,7 @@ struct PropertiesView: View { // MARK: - Property Card struct PropertyCard: View { + @Environment(\.reduceTransparency) private var reduceTransparency let property: PropertyInfo var body: some View { @@ -72,7 +74,7 @@ struct PropertyCard: View { .foregroundColor(.orange) .padding(.horizontal, 6) .padding(.vertical, 2) - .background(Color.orange.opacity(0.2)) + .background(Color.orange.opacity(reduceTransparency ? 0.5 : 0.2)) .cornerRadius(4) } } @@ -112,7 +114,7 @@ struct PropertyCard: View { } } .padding() - .background(Color.brown.opacity(0.05)) + .background(Color.brown.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) } diff --git a/MacTorn/MacTorn/Views/SettingsView.swift b/MacTorn/MacTorn/Views/SettingsView.swift index b28f42d..f0ca1e7 100644 --- a/MacTorn/MacTorn/Views/SettingsView.swift +++ b/MacTorn/MacTorn/Views/SettingsView.swift @@ -2,6 +2,8 @@ import SwiftUI struct SettingsView: View { @EnvironmentObject var appState: AppState + @AppStorage("appearanceMode") private var appearanceMode: String = AppearanceMode.system.rawValue + @AppStorage("reduceTransparency") private var reduceTransparency: Bool = false @State private var inputKey: String = "" @State private var showCredits: Bool = false @@ -88,6 +90,30 @@ struct SettingsView: View { )) .toggleStyle(.switch) } + + // Appearance Mode + HStack { + Image(systemName: "moon.circle") + .foregroundColor(.secondary) + .frame(width: 20) + + Picker("Appearance", selection: $appearanceMode) { + ForEach(AppearanceMode.allCases, id: \.self) { mode in + Text(mode.rawValue).tag(mode.rawValue) + } + } + .pickerStyle(.segmented) + .labelsHidden() + } + + // Reduce Transparency (Accessibility) + HStack { + Image(systemName: "eye") + .foregroundColor(.secondary) + .frame(width: 20) + Toggle("Reduce Transparency", isOn: $reduceTransparency) + .toggleStyle(.switch) + } } .padding(.horizontal) @@ -116,16 +142,16 @@ struct SettingsView: View { .font(.caption) .padding(.vertical, 6) .padding(.horizontal, 12) - .background(Color.purple.opacity(0.15)) + .background(Color.purple.opacity(reduceTransparency ? 0.4 : 0.15)) .cornerRadius(6) } .buttonStyle(.plain) } .padding(.vertical, 8) .padding(.horizontal) - .background(Color.purple.opacity(0.05)) + .background(Color.purple.opacity(reduceTransparency ? 0.25 : 0.05)) .cornerRadius(8) - + // Update Section if let update = appState.updateAvailable { VStack(spacing: 8) { @@ -146,7 +172,7 @@ struct SettingsView: View { } .padding(10) .frame(maxWidth: .infinity) - .background(Color.green.opacity(0.1)) + .background(Color.green.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(8) } diff --git a/MacTorn/MacTorn/Views/StatusView.swift b/MacTorn/MacTorn/Views/StatusView.swift index 5949748..a94bd11 100644 --- a/MacTorn/MacTorn/Views/StatusView.swift +++ b/MacTorn/MacTorn/Views/StatusView.swift @@ -2,6 +2,7 @@ import SwiftUI struct StatusView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency var body: some View { ScrollView { @@ -109,7 +110,7 @@ struct StatusView: View { } .padding(.horizontal, 10) .padding(.vertical, 6) - .background(Color.blue.opacity(0.1)) + .background(Color.blue.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(6) } .buttonStyle(.plain) @@ -150,7 +151,7 @@ struct StatusView: View { } .padding(8) .frame(maxWidth: .infinity, alignment: .leading) - .background(Color.blue.opacity(0.1)) + .background(Color.blue.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(8) .transaction { $0.animation = nil } } @@ -240,7 +241,7 @@ struct StatusView: View { .frame(maxWidth: .infinity) .padding(.vertical, 4) .padding(.horizontal, 6) - .background(Color.accentColor.opacity(0.1)) + .background(Color.accentColor.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(4) } .buttonStyle(.plain) diff --git a/MacTorn/MacTorn/Views/TravelView.swift b/MacTorn/MacTorn/Views/TravelView.swift index e254ae6..0169c07 100644 --- a/MacTorn/MacTorn/Views/TravelView.swift +++ b/MacTorn/MacTorn/Views/TravelView.swift @@ -4,6 +4,7 @@ import AppKit // MARK: - Flying Status View (separate for proper live updates) struct FlyingStatusView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency let destination: String let timestamp: Int let departed: Int @@ -60,7 +61,7 @@ struct FlyingStatusView: View { GeometryReader { geometry in ZStack(alignment: .leading) { RoundedRectangle(cornerRadius: 4) - .fill(Color.gray.opacity(0.2)) + .fill(Color.gray.opacity(reduceTransparency ? 0.5 : 0.2)) .frame(height: 8) RoundedRectangle(cornerRadius: 4) @@ -72,7 +73,7 @@ struct FlyingStatusView: View { } } .padding() - .background(Color.blue.opacity(0.1)) + .background(Color.blue.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(12) .transaction { $0.animation = nil } } @@ -80,6 +81,7 @@ struct FlyingStatusView: View { struct TravelView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency var body: some View { ScrollView { @@ -164,7 +166,7 @@ struct TravelView: View { .buttonStyle(.plain) } .padding() - .background(Color.orange.opacity(0.1)) + .background(Color.orange.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(12) } @@ -183,7 +185,7 @@ struct TravelView: View { Spacer() } .padding() - .background(Color.green.opacity(0.1)) + .background(Color.green.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(12) } @@ -212,7 +214,7 @@ struct TravelView: View { } .frame(maxWidth: .infinity) .padding(.vertical, 10) - .background(Color.accentColor.opacity(0.1)) + .background(Color.accentColor.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(8) } .buttonStyle(.plain) @@ -248,7 +250,7 @@ struct TravelView: View { } .frame(maxWidth: .infinity) .padding(.vertical, 8) - .background(Color.accentColor.opacity(0.1)) + .background(Color.accentColor.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(8) } .buttonStyle(.plain) @@ -283,7 +285,7 @@ struct TravelView: View { } } .padding() - .background(Color.secondary.opacity(0.1)) + .background(Color.secondary.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(8) } } @@ -312,7 +314,7 @@ struct TravelView: View { .font(.caption) .frame(maxWidth: .infinity) .padding(.vertical, 8) - .background(Color.accentColor.opacity(0.1)) + .background(Color.accentColor.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(6) } .buttonStyle(.plain) @@ -329,7 +331,7 @@ struct TravelView: View { .font(.caption) .frame(maxWidth: .infinity) .padding(.vertical, 8) - .background(Color.accentColor.opacity(0.1)) + .background(Color.accentColor.opacity(reduceTransparency ? 0.2 : 0.1)) .cornerRadius(6) } .buttonStyle(.plain) diff --git a/MacTorn/MacTorn/Views/WatchlistView.swift b/MacTorn/MacTorn/Views/WatchlistView.swift index 99408cc..f1fb764 100644 --- a/MacTorn/MacTorn/Views/WatchlistView.swift +++ b/MacTorn/MacTorn/Views/WatchlistView.swift @@ -2,6 +2,7 @@ import SwiftUI struct WatchlistView: View { @EnvironmentObject var appState: AppState + @Environment(\.reduceTransparency) private var reduceTransparency @State private var showAddItem = false var body: some View { @@ -56,7 +57,7 @@ struct WatchlistView: View { .lineLimit(1) .frame(maxWidth: .infinity) .padding(.vertical, 6) - .background(Color.green.opacity(0.1)) + .background(Color.green.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(4) } .buttonStyle(.plain) @@ -64,10 +65,10 @@ struct WatchlistView: View { } } .padding(8) - .background(Color.gray.opacity(0.1)) + .background(Color.gray.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(6) } - + // Watchlist Items with Prices if appState.watchlistItems.isEmpty && !showAddItem { VStack(spacing: 8) { @@ -132,6 +133,7 @@ struct WatchlistView: View { // MARK: - Watchlist Price Row struct WatchlistPriceRow: View { + @Environment(\.reduceTransparency) private var reduceTransparency let item: WatchlistItem let onOpen: () -> Void let onRemove: () -> Void @@ -200,10 +202,10 @@ struct WatchlistPriceRow: View { .buttonStyle(.plain) } .padding(8) - .background(Color.gray.opacity(0.1)) + .background(Color.gray.opacity(reduceTransparency ? 0.4 : 0.1)) .cornerRadius(6) } - + private func formatPrice(_ price: Int) -> String { if price >= 1_000_000 { return String(format: "$%.1fM", Double(price) / 1_000_000) diff --git a/README.md b/README.md index c0b318e..37a3263 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ A native macOS menu bar app for monitoring your **Torn** game status. ![License](https://img.shields.io/badge/License-MIT-green)

- MacTorn Screenshot + MacTorn Light Mode +    + MacTorn Dark Mode

## Features @@ -57,6 +59,14 @@ A native macOS menu bar app for monitoring your **Torn** game status. - **🚀 Launch at Login**: Start seamlessly with macOS. - **⚡️ Optimized Startup**: Non-blocking data fetching for instant UI responsiveness. +## Accessibility + +MacTorn respects macOS accessibility settings: + +- **Reduce Transparency**: When enabled in System Settings → Accessibility → Display, the app uses solid backgrounds instead of translucent materials for better readability +- **Light & Dark Mode**: Full support for both appearance modes with optimized contrast +- **Color-coded indicators**: Status bars and badges use distinct colors that work well in both modes + ## Installation 1. Download the latest release from [Releases](https://github.com/pawelorzech/MacTorn/releases) diff --git a/app.png b/app.png deleted file mode 100644 index 559c6b0..0000000 Binary files a/app.png and /dev/null differ diff --git a/app_dark_1.png b/app_dark_1.png new file mode 100644 index 0000000..7337f1f Binary files /dev/null and b/app_dark_1.png differ diff --git a/app_light_1.png b/app_light_1.png new file mode 100644 index 0000000..bfa7870 Binary files /dev/null and b/app_light_1.png differ