From 18e02e1851028a0b815a0d2245f00b44eae2afe3 Mon Sep 17 00:00:00 2001 From: Stephen B <158498287+StephenDev0@users.noreply.github.com> Date: Wed, 4 Jun 2025 00:44:42 -0400 Subject: [PATCH 1/4] Remove unsupported Vision screenshot and update theme button --- StikJIT/StikJITApp.swift | 6 ++ StikJIT/Views/ConsoleLogsView.swift | 13 +++- StikJIT/Views/DisplayView.swift | 101 +++++++++++++++++++++++----- StikJIT/Views/HomeView.swift | 13 +++- StikJIT/Views/SettingsView.swift | 23 ++++++- 5 files changed, 131 insertions(+), 25 deletions(-) diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index 1d5bbd98..086fdd6c 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -475,6 +475,8 @@ struct HeartbeatApp: App { window.overrideUserInterfaceStyle = .dark case "light": window.overrideUserInterfaceStyle = .light + case "vision": + window.overrideUserInterfaceStyle = .light default: window.overrideUserInterfaceStyle = .unspecified } @@ -507,6 +509,8 @@ struct HeartbeatApp: App { window.overrideUserInterfaceStyle = .dark case "light": window.overrideUserInterfaceStyle = .light + case "vision": + window.overrideUserInterfaceStyle = .light default: window.overrideUserInterfaceStyle = .unspecified } @@ -871,6 +875,8 @@ struct LoadingView: View { return true case "light": return false + case "vision": + return false default: return colorScheme == .dark } diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index ad7c91ae..fbea02b3 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -16,6 +16,7 @@ struct ConsoleLogsView: View { @State private var autoScroll = true @State private var scrollView: ScrollViewProxy? = nil @AppStorage("customAccentColor") private var customAccentColorHex: String = "" + @AppStorage("appTheme") private var appTheme: String = "system" // Alert handling @State private var showingCustomAlert = false @@ -43,9 +44,15 @@ struct ConsoleLogsView: View { var body: some View { NavigationView { ZStack { - // Use system background color instead of fixed black - Color(colorScheme == .dark ? .black : .white) - .edgesIgnoringSafeArea(.all) + if appTheme == "vision" { + Color.clear + .background(.ultraThinMaterial) + .edgesIgnoringSafeArea(.all) + } else { + // Use system background color instead of fixed black + Color(colorScheme == .dark ? .black : .white) + .edgesIgnoringSafeArea(.all) + } VStack(spacing: 0) { // Terminal logs area with theme support diff --git a/StikJIT/Views/DisplayView.swift b/StikJIT/Views/DisplayView.swift index 2fd2e90f..5a7c9b6b 100644 --- a/StikJIT/Views/DisplayView.swift +++ b/StikJIT/Views/DisplayView.swift @@ -77,8 +77,14 @@ struct DisplayView: View { var body: some View { ZStack { - Color(colorScheme == .dark ? .black : UIColor.systemBackground) - .ignoresSafeArea() + if appTheme == "vision" { + Color.clear + .background(.ultraThinMaterial) + .ignoresSafeArea() + } else { + Color(colorScheme == .dark ? .black : UIColor.systemBackground) + .ignoresSafeArea() + } ScrollView { VStack(spacing: 16) { @@ -118,6 +124,15 @@ struct DisplayView: View { accentColor: selectedAccentColor, action: { appTheme = "dark" } ) + + // Vision Theme + ThemeOptionButton( + title: "Vision", + systemImageName: "eye", + isSelected: appTheme == "vision", + accentColor: selectedAccentColor, + action: { appTheme = "vision" } + ) } .onChange(of: appTheme) { newValue in applyTheme(newValue) @@ -137,7 +152,15 @@ struct DisplayView: View { } .padding(.vertical, 16) .padding(.horizontal, 16) - .background(Color(UIColor.systemGray6)) + .background( + Group { + if appTheme == "vision" { + Color.clear.background(.ultraThinMaterial) + } else { + Color(UIColor.systemGray6) + } + } + ) .cornerRadius(16) // Username Section @@ -167,7 +190,15 @@ struct DisplayView: View { } .padding(.vertical, 16) .padding(.horizontal, 16) - .background(Color(UIColor.systemGray6)) + .background( + Group { + if appTheme == "vision" { + Color.clear.background(.ultraThinMaterial) + } else { + Color(UIColor.systemGray6) + } + } + ) .cornerRadius(16) // JIT Options Section @@ -189,7 +220,15 @@ struct DisplayView: View { } .padding(.vertical, 16) .padding(.horizontal, 16) - .background(Color(UIColor.systemGray6)) + .background( + Group { + if appTheme == "vision" { + Color.clear.background(.ultraThinMaterial) + } else { + Color(UIColor.systemGray6) + } + } + ) .cornerRadius(16) } .padding(.horizontal, 16) @@ -225,6 +264,8 @@ struct DisplayView: View { window.overrideUserInterfaceStyle = .dark case "light": window.overrideUserInterfaceStyle = .light + case "vision": + window.overrideUserInterfaceStyle = .light default: window.overrideUserInterfaceStyle = .unspecified } @@ -234,24 +275,52 @@ struct DisplayView: View { struct ThemeOptionButton: View { let title: String - let imageName: String + let imageName: String? + let systemImageName: String? let isSelected: Bool let accentColor: Color let action: () -> Void + + init(title: String, + imageName: String? = nil, + systemImageName: String? = nil, + isSelected: Bool, + accentColor: Color, + action: @escaping () -> Void) { + self.title = title + self.imageName = imageName + self.systemImageName = systemImageName + self.isSelected = isSelected + self.accentColor = accentColor + self.action = action + } var body: some View { Button(action: action) { VStack(spacing: 8) { - Image(imageName) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(height: 160) - .cornerRadius(8) - .overlay( - RoundedRectangle(cornerRadius: 8) - .stroke(isSelected ? accentColor : Color.clear, lineWidth: 2) - ) - + if let name = imageName { + Image(name) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 160) + .cornerRadius(8) + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(isSelected ? accentColor : Color.clear, lineWidth: 2) + ) + } else if let systemName = systemImageName { + Image(systemName: systemName) + .resizable() + .scaledToFit() + .frame(height: 80) + .padding() + .foregroundColor(isSelected ? accentColor : .primary) + .background( + RoundedRectangle(cornerRadius: 8) + .stroke(isSelected ? accentColor : Color.primary.opacity(0.1), lineWidth: 2) + ) + } + Text(title) .font(.caption) .foregroundColor(isSelected ? accentColor : .primary) diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index cdd05625..f77f42e4 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -39,6 +39,7 @@ struct HomeView: View { @State private var viewDidAppeared = false @State private var pendingBundleIdToEnableJIT : String? = nil @State private var pendingPIDToEnableJIT : Int? = nil + @AppStorage("appTheme") private var appTheme: String = "system" private var accentColor: Color { if customAccentColorHex.isEmpty { @@ -50,9 +51,15 @@ struct HomeView: View { var body: some View { ZStack { - // Use system background - Color(colorScheme == .dark ? .black : .white) - .edgesIgnoringSafeArea(.all) + if appTheme == "vision" { + Color.clear + .background(.ultraThinMaterial) + .edgesIgnoringSafeArea(.all) + } else { + // Use system background + Color(colorScheme == .dark ? .black : .white) + .edgesIgnoringSafeArea(.all) + } VStack(spacing: 25) { Spacer() diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index 518ee678..a35e3dbd 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -12,6 +12,7 @@ struct SettingsView: View { @AppStorage("customAccentColor") private var customAccentColorHex: String = "" @AppStorage("selectedAppIcon") private var selectedAppIcon: String = "AppIcon" @AppStorage("autoQuitAfterEnablingJIT") private var doAutoQuitAfterEnablingJIT = false + @AppStorage("appTheme") private var appTheme: String = "system" @State private var isShowingPairingFilePicker = false @Environment(\.colorScheme) private var colorScheme @@ -58,8 +59,14 @@ struct SettingsView: View { var body: some View { ZStack { - Color(UIColor.systemBackground) - .ignoresSafeArea() + if appTheme == "vision" { + Color.clear + .background(.ultraThinMaterial) + .ignoresSafeArea() + } else { + Color(UIColor.systemBackground) + .ignoresSafeArea() + } ScrollView { VStack(spacing: 12) { @@ -617,6 +624,8 @@ struct SettingsView: View { struct SettingsCard: View { let content: Content + + @AppStorage("appTheme") private var appTheme: String = "system" init(@ViewBuilder content: () -> Content) { self.content = content() @@ -624,7 +633,15 @@ struct SettingsCard: View { var body: some View { content - .background(Color(UIColor.secondarySystemBackground)) + .background( + Group { + if appTheme == "vision" { + Color.clear.background(.ultraThinMaterial) + } else { + Color(UIColor.secondarySystemBackground) + } + } + ) .cornerRadius(16) .shadow(color: Color.black.opacity(0.08), radius: 3, x: 0, y: 2) } From f04e76a2d2186bd081083d0726adcf908733c1ea Mon Sep 17 00:00:00 2001 From: Stephen B <158498287+StephenDev0@users.noreply.github.com> Date: Wed, 4 Jun 2025 00:51:07 -0400 Subject: [PATCH 2/4] Refine Vision theme with dark mode --- StikJIT/StikJITApp.swift | 6 +++--- StikJIT/Views/DisplayView.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/StikJIT/StikJITApp.swift b/StikJIT/StikJITApp.swift index 086fdd6c..4f7ad9d0 100644 --- a/StikJIT/StikJITApp.swift +++ b/StikJIT/StikJITApp.swift @@ -476,7 +476,7 @@ struct HeartbeatApp: App { case "light": window.overrideUserInterfaceStyle = .light case "vision": - window.overrideUserInterfaceStyle = .light + window.overrideUserInterfaceStyle = .dark default: window.overrideUserInterfaceStyle = .unspecified } @@ -510,7 +510,7 @@ struct HeartbeatApp: App { case "light": window.overrideUserInterfaceStyle = .light case "vision": - window.overrideUserInterfaceStyle = .light + window.overrideUserInterfaceStyle = .dark default: window.overrideUserInterfaceStyle = .unspecified } @@ -876,7 +876,7 @@ struct LoadingView: View { case "light": return false case "vision": - return false + return true default: return colorScheme == .dark } diff --git a/StikJIT/Views/DisplayView.swift b/StikJIT/Views/DisplayView.swift index 5a7c9b6b..4922bafc 100644 --- a/StikJIT/Views/DisplayView.swift +++ b/StikJIT/Views/DisplayView.swift @@ -265,7 +265,7 @@ struct DisplayView: View { case "light": window.overrideUserInterfaceStyle = .light case "vision": - window.overrideUserInterfaceStyle = .light + window.overrideUserInterfaceStyle = .dark default: window.overrideUserInterfaceStyle = .unspecified } From 6f94b7e2e8e4464e7bf4d6ff29385be8fd9e134b Mon Sep 17 00:00:00 2001 From: Stephen <158498287+StephenDev0@users.noreply.github.com> Date: Wed, 4 Jun 2025 00:51:37 -0400 Subject: [PATCH 3/4] Remove the StikVPN target --- StikDebug.xcodeproj/project.pbxproj | 363 ------------------ .../AccentColor.colorset/Contents.json | 11 - .../AppIcon.appiconset/Contents.json | 35 -- StikVPN/Assets.xcassets/Contents.json | 6 - StikVPN/ContentView.swift | 24 -- .../Preview Assets.xcassets/Contents.json | 6 - StikVPN/StikVPNApp.swift | 17 - StikVPNTests/StikVPNTests.swift | 17 - StikVPNUITests/StikVPNUITests.swift | 43 --- .../StikVPNUITestsLaunchTests.swift | 33 -- 10 files changed, 555 deletions(-) delete mode 100644 StikVPN/Assets.xcassets/AccentColor.colorset/Contents.json delete mode 100644 StikVPN/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 StikVPN/Assets.xcassets/Contents.json delete mode 100644 StikVPN/ContentView.swift delete mode 100644 StikVPN/Preview Content/Preview Assets.xcassets/Contents.json delete mode 100644 StikVPN/StikVPNApp.swift delete mode 100644 StikVPNTests/StikVPNTests.swift delete mode 100644 StikVPNUITests/StikVPNUITests.swift delete mode 100644 StikVPNUITests/StikVPNUITestsLaunchTests.swift diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj index dcd3f4ef..4a85ee65 100644 --- a/StikDebug.xcodeproj/project.pbxproj +++ b/StikDebug.xcodeproj/project.pbxproj @@ -22,20 +22,6 @@ remoteGlobalIDString = DC139F6B2DE97EA400F63846; remoteInfo = DebugWidgetExtension; }; - DC32875A2DEFF3AD005B2F11 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = DC6F1D2F2D94EADD0071B2B6 /* Project object */; - proxyType = 1; - remoteGlobalIDString = DC3287492DEFF3AB005B2F11; - remoteInfo = StikVPN; - }; - DC3287642DEFF3AD005B2F11 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = DC6F1D2F2D94EADD0071B2B6 /* Project object */; - proxyType = 1; - remoteGlobalIDString = DC3287492DEFF3AB005B2F11; - remoteInfo = StikVPN; - }; DC6F1D492D94EADF0071B2B6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = DC6F1D2F2D94EADD0071B2B6 /* Project object */; @@ -79,9 +65,6 @@ DC139F6D2DE97EA400F63846 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; DC139F6F2DE97EA400F63846 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; DC139F862DE97F2000F63846 /* DebugWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugWidgetExtension.entitlements; sourceTree = ""; }; - DC32874A2DEFF3AB005B2F11 /* StikVPN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StikVPN.app; sourceTree = BUILT_PRODUCTS_DIR; }; - DC3287592DEFF3AD005B2F11 /* StikVPNTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikVPNTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - DC3287632DEFF3AD005B2F11 /* StikVPNUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikVPNUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D372D94EADD0071B2B6 /* StikDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StikDebug.app; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D482D94EADF0071B2B6 /* StikDebugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -123,21 +106,6 @@ path = DebugWidget; sourceTree = ""; }; - DC32874B2DEFF3AB005B2F11 /* StikVPN */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = StikVPN; - sourceTree = ""; - }; - DC32875C2DEFF3AD005B2F11 /* StikVPNTests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = StikVPNTests; - sourceTree = ""; - }; - DC3287662DEFF3AD005B2F11 /* StikVPNUITests */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = StikVPNUITests; - sourceTree = ""; - }; DC6F1D392D94EADD0071B2B6 /* StikJIT */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( @@ -176,27 +144,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - DC3287472DEFF3AB005B2F11 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DC3287562DEFF3AD005B2F11 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DC3287602DEFF3AD005B2F11 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; DC6F1D342D94EADD0071B2B6 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -238,9 +185,6 @@ DC6F1D552D94EADF0071B2B6 /* StikJITUITests */, DCA690132DAF660E007C91A8 /* TunnelProv */, DC139F712DE97EA400F63846 /* DebugWidget */, - DC32874B2DEFF3AB005B2F11 /* StikVPN */, - DC32875C2DEFF3AD005B2F11 /* StikVPNTests */, - DC3287662DEFF3AD005B2F11 /* StikVPNUITests */, DC6F1D752D94EB620071B2B6 /* Frameworks */, DC6F1D382D94EADD0071B2B6 /* Products */, ); @@ -254,9 +198,6 @@ DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */, DCA690102DAF660E007C91A8 /* TunnelProv.appex */, DC139F6C2DE97EA400F63846 /* DebugWidgetExtension.appex */, - DC32874A2DEFF3AB005B2F11 /* StikVPN.app */, - DC3287592DEFF3AD005B2F11 /* StikVPNTests.xctest */, - DC3287632DEFF3AD005B2F11 /* StikVPNUITests.xctest */, ); name = Products; sourceTree = ""; @@ -296,74 +237,6 @@ productReference = DC139F6C2DE97EA400F63846 /* DebugWidgetExtension.appex */; productType = "com.apple.product-type.app-extension"; }; - DC3287492DEFF3AB005B2F11 /* StikVPN */ = { - isa = PBXNativeTarget; - buildConfigurationList = DC3287712DEFF3AD005B2F11 /* Build configuration list for PBXNativeTarget "StikVPN" */; - buildPhases = ( - DC3287462DEFF3AB005B2F11 /* Sources */, - DC3287472DEFF3AB005B2F11 /* Frameworks */, - DC3287482DEFF3AB005B2F11 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - DC32874B2DEFF3AB005B2F11 /* StikVPN */, - ); - name = StikVPN; - packageProductDependencies = ( - ); - productName = StikVPN; - productReference = DC32874A2DEFF3AB005B2F11 /* StikVPN.app */; - productType = "com.apple.product-type.application"; - }; - DC3287582DEFF3AD005B2F11 /* StikVPNTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = DC3287722DEFF3AD005B2F11 /* Build configuration list for PBXNativeTarget "StikVPNTests" */; - buildPhases = ( - DC3287552DEFF3AD005B2F11 /* Sources */, - DC3287562DEFF3AD005B2F11 /* Frameworks */, - DC3287572DEFF3AD005B2F11 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - DC32875B2DEFF3AD005B2F11 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - DC32875C2DEFF3AD005B2F11 /* StikVPNTests */, - ); - name = StikVPNTests; - packageProductDependencies = ( - ); - productName = StikVPNTests; - productReference = DC3287592DEFF3AD005B2F11 /* StikVPNTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - DC3287622DEFF3AD005B2F11 /* StikVPNUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = DC3287732DEFF3AD005B2F11 /* Build configuration list for PBXNativeTarget "StikVPNUITests" */; - buildPhases = ( - DC32875F2DEFF3AD005B2F11 /* Sources */, - DC3287602DEFF3AD005B2F11 /* Frameworks */, - DC3287612DEFF3AD005B2F11 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - DC3287652DEFF3AD005B2F11 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - DC3287662DEFF3AD005B2F11 /* StikVPNUITests */, - ); - name = StikVPNUITests; - packageProductDependencies = ( - ); - productName = StikVPNUITests; - productReference = DC3287632DEFF3AD005B2F11 /* StikVPNUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; DC6F1D362D94EADD0071B2B6 /* StikDebug */ = { isa = PBXNativeTarget; buildConfigurationList = DC6F1D5C2D94EADF0071B2B6 /* Build configuration list for PBXNativeTarget "StikDebug" */; @@ -470,17 +343,6 @@ DC139F6B2DE97EA400F63846 = { CreatedOnToolsVersion = 16.2; }; - DC3287492DEFF3AB005B2F11 = { - CreatedOnToolsVersion = 16.2; - }; - DC3287582DEFF3AD005B2F11 = { - CreatedOnToolsVersion = 16.2; - TestTargetID = DC3287492DEFF3AB005B2F11; - }; - DC3287622DEFF3AD005B2F11 = { - CreatedOnToolsVersion = 16.2; - TestTargetID = DC3287492DEFF3AB005B2F11; - }; DC6F1D362D94EADD0071B2B6 = { CreatedOnToolsVersion = 16.2; LastSwiftMigration = 1620; @@ -517,9 +379,6 @@ DC6F1D512D94EADF0071B2B6 /* StikDebugUITests */, DCA6900F2DAF660E007C91A8 /* TunnelProv */, DC139F6B2DE97EA400F63846 /* DebugWidgetExtension */, - DC3287492DEFF3AB005B2F11 /* StikVPN */, - DC3287582DEFF3AD005B2F11 /* StikVPNTests */, - DC3287622DEFF3AD005B2F11 /* StikVPNUITests */, ); }; /* End PBXProject section */ @@ -532,27 +391,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - DC3287482DEFF3AB005B2F11 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DC3287572DEFF3AD005B2F11 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DC3287612DEFF3AD005B2F11 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; DC6F1D352D94EADD0071B2B6 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -591,27 +429,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - DC3287462DEFF3AB005B2F11 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DC3287552DEFF3AD005B2F11 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - DC32875F2DEFF3AD005B2F11 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; DC6F1D332D94EADD0071B2B6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -648,16 +465,6 @@ target = DC139F6B2DE97EA400F63846 /* DebugWidgetExtension */; targetProxy = DC139F7F2DE97EA600F63846 /* PBXContainerItemProxy */; }; - DC32875B2DEFF3AD005B2F11 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = DC3287492DEFF3AB005B2F11 /* StikVPN */; - targetProxy = DC32875A2DEFF3AD005B2F11 /* PBXContainerItemProxy */; - }; - DC3287652DEFF3AD005B2F11 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = DC3287492DEFF3AB005B2F11 /* StikVPN */; - targetProxy = DC3287642DEFF3AD005B2F11 /* PBXContainerItemProxy */; - }; DC6F1D4A2D94EADF0071B2B6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = DC6F1D362D94EADD0071B2B6 /* StikDebug */; @@ -737,149 +544,6 @@ }; name = Release; }; - DC32876B2DEFF3AD005B2F11 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"StikVPN/Preview Content\""; - DEVELOPMENT_TEAM = SZ977XLF24; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.StikVPN; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - DC32876C2DEFF3AD005B2F11 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"StikVPN/Preview Content\""; - DEVELOPMENT_TEAM = SZ977XLF24; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.StikVPN; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - DC32876D2DEFF3AD005B2F11 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = SZ977XLF24; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.StikVPNTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StikVPN.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/StikVPN"; - }; - name = Debug; - }; - DC32876E2DEFF3AD005B2F11 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = SZ977XLF24; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.StikVPNTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StikVPN.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/StikVPN"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - DC32876F2DEFF3AD005B2F11 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = SZ977XLF24; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.StikVPNUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = StikVPN; - }; - name = Debug; - }; - DC3287702DEFF3AD005B2F11 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = SZ977XLF24; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.StikVPNUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = StikVPN; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; DC6F1D5A2D94EADF0071B2B6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1250,33 +914,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - DC3287712DEFF3AD005B2F11 /* Build configuration list for PBXNativeTarget "StikVPN" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DC32876B2DEFF3AD005B2F11 /* Debug */, - DC32876C2DEFF3AD005B2F11 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - DC3287722DEFF3AD005B2F11 /* Build configuration list for PBXNativeTarget "StikVPNTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DC32876D2DEFF3AD005B2F11 /* Debug */, - DC32876E2DEFF3AD005B2F11 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - DC3287732DEFF3AD005B2F11 /* Build configuration list for PBXNativeTarget "StikVPNUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DC32876F2DEFF3AD005B2F11 /* Debug */, - DC3287702DEFF3AD005B2F11 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; DC6F1D322D94EADD0071B2B6 /* Build configuration list for PBXProject "StikDebug" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/StikVPN/Assets.xcassets/AccentColor.colorset/Contents.json b/StikVPN/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/StikVPN/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/StikVPN/Assets.xcassets/AppIcon.appiconset/Contents.json b/StikVPN/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 23058801..00000000 --- a/StikVPN/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/StikVPN/Assets.xcassets/Contents.json b/StikVPN/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/StikVPN/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/StikVPN/ContentView.swift b/StikVPN/ContentView.swift deleted file mode 100644 index 32c70714..00000000 --- a/StikVPN/ContentView.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ContentView.swift -// StikVPN -// -// Created by Stephen on 6/3/25. -// - -import SwiftUI - -struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") - } - .padding() - } -} - -#Preview { - ContentView() -} diff --git a/StikVPN/Preview Content/Preview Assets.xcassets/Contents.json b/StikVPN/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/StikVPN/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/StikVPN/StikVPNApp.swift b/StikVPN/StikVPNApp.swift deleted file mode 100644 index a7cae506..00000000 --- a/StikVPN/StikVPNApp.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// StikVPNApp.swift -// StikVPN -// -// Created by Stephen on 6/3/25. -// - -import SwiftUI - -@main -struct StikVPNApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} diff --git a/StikVPNTests/StikVPNTests.swift b/StikVPNTests/StikVPNTests.swift deleted file mode 100644 index 89cf2147..00000000 --- a/StikVPNTests/StikVPNTests.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// StikVPNTests.swift -// StikVPNTests -// -// Created by Stephen on 6/3/25. -// - -import Testing -@testable import StikVPN - -struct StikVPNTests { - - @Test func example() async throws { - // Write your test here and use APIs like `#expect(...)` to check expected conditions. - } - -} diff --git a/StikVPNUITests/StikVPNUITests.swift b/StikVPNUITests/StikVPNUITests.swift deleted file mode 100644 index 0e8a2d2f..00000000 --- a/StikVPNUITests/StikVPNUITests.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// StikVPNUITests.swift -// StikVPNUITests -// -// Created by Stephen on 6/3/25. -// - -import XCTest - -final class StikVPNUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - @MainActor - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - @MainActor - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/StikVPNUITests/StikVPNUITestsLaunchTests.swift b/StikVPNUITests/StikVPNUITestsLaunchTests.swift deleted file mode 100644 index 6d2dc9f2..00000000 --- a/StikVPNUITests/StikVPNUITestsLaunchTests.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// StikVPNUITestsLaunchTests.swift -// StikVPNUITests -// -// Created by Stephen on 6/3/25. -// - -import XCTest - -final class StikVPNUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - @MainActor - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} From bc672507b03e697823fa70f938fd46fd70fe9f85 Mon Sep 17 00:00:00 2001 From: Stephen B <158498287+StephenDev0@users.noreply.github.com> Date: Wed, 4 Jun 2025 00:58:00 -0400 Subject: [PATCH 4/4] Integrate SwiftGlass for Vision theme --- StikJIT/SwiftGlass/BlobGlassModifier.swift | 182 ++++++++++++++++++ .../SwiftGlass/GlassBackgroundModifier.swift | 112 +++++++++++ StikJIT/SwiftGlass/SwiftGlass.swift | 107 ++++++++++ StikJIT/Views/ConsoleLogsView.swift | 3 +- StikJIT/Views/DisplayView.swift | 9 +- StikJIT/Views/HomeView.swift | 3 +- StikJIT/Views/SettingsView.swift | 5 +- 7 files changed, 413 insertions(+), 8 deletions(-) create mode 100644 StikJIT/SwiftGlass/BlobGlassModifier.swift create mode 100644 StikJIT/SwiftGlass/GlassBackgroundModifier.swift create mode 100644 StikJIT/SwiftGlass/SwiftGlass.swift diff --git a/StikJIT/SwiftGlass/BlobGlassModifier.swift b/StikJIT/SwiftGlass/BlobGlassModifier.swift new file mode 100644 index 00000000..d1a52559 --- /dev/null +++ b/StikJIT/SwiftGlass/BlobGlassModifier.swift @@ -0,0 +1,182 @@ +// +// BlobShape.swift +// SwiftGlass +// +// Created by Ming on 21/4/2025. +// + +import SwiftUI + +@available(iOS 15.0, macOS 14.0, watchOS 10.0, tvOS 15.0, visionOS 1.0, *) +public struct BlobGlassModifier: ViewModifier { + // Parameters + let color: Color + let blobIntensity: CGFloat + let animationSpeed: Double + let complexity: Int + let material: Material + let gradientOpacity: Double + let shadowOpacity: Double + + // Animation state + @State private var animationProgress: CGFloat = 0.0 + + // Initializer + public init( + color: Color, + blobIntensity: CGFloat, + animationSpeed: Double, + complexity: Int, + material: Material, + gradientOpacity: Double, + shadowOpacity: Double + ) { + self.color = color + self.blobIntensity = min(max(blobIntensity, 0.1), 1.0) // Constrain between 0.1 and 1.0 + self.animationSpeed = animationSpeed + self.complexity = min(max(complexity, 4), 12) // Constrain between 4 and 12 + self.material = material + self.gradientOpacity = gradientOpacity + self.shadowOpacity = shadowOpacity + } + + // ViewModifier Body + public func body(content: Content) -> some View { + content + // Clip content to animated blob shape + .clipShape( + BlobShape( + complexity: complexity, + animationProgress: animationProgress, + intensity: blobIntensity + ) + ) + // Blob glass background and effects + .background( + BlobShape( + complexity: complexity, + animationProgress: animationProgress, + intensity: blobIntensity + ) + .fill(material) + .overlay( + BlobShape( + complexity: complexity, + animationProgress: animationProgress, + intensity: blobIntensity + ) + .stroke( + LinearGradient( + colors: [ + color.opacity(gradientOpacity), + color.opacity(0), + color.opacity(gradientOpacity), + color.opacity(0) + ], + startPoint: .topLeading, + endPoint: .bottomTrailing + ), + lineWidth: 1.5 + ) + ) + .shadow( + color: color.opacity(shadowOpacity), + radius: 10, + x: 0, + y: 5 + ) + ) + .onAppear { + // Start the continuous animation + withAnimation( + Animation + .linear(duration: 10 / animationSpeed) + .repeatForever(autoreverses: true) + ) { + animationProgress = 1.0 + } + } + } +} + +// BlobShape +@available(iOS 15.0, macOS 14.0, watchOS 10.0, tvOS 15.0, visionOS 1.0, *) +public struct BlobShape: Shape { + // Control points for the blob's perimeter + var controlPoints: [UnitPoint] + var animationProgress: CGFloat + var intensity: CGFloat + + // Initializer + public init(complexity: Int = 8, animationProgress: CGFloat = 0.0, intensity: CGFloat = 0.5) { + self.controlPoints = BlobShape.generateControlPoints(count: complexity) + self.animationProgress = animationProgress + self.intensity = intensity + } + + // Animatable + public var animatableData: CGFloat { + get { animationProgress } + set { animationProgress = newValue } + } + + // Path Generation + public func path(in rect: CGRect) -> Path { + let center = CGPoint(x: rect.midX, y: rect.midY) + let minDimension = min(rect.width, rect.height) + let radius = minDimension / 2 + + // Calculate points on the blob's perimeter + let points = controlPoints.map { unitPoint -> CGPoint in + let angle = 2 * .pi * unitPoint.x + (animationProgress * 2 * .pi) + let distortionAmount = sin(angle * 3 + animationProgress * 4) * intensity * 0.2 + let blobRadius = radius * (1 + distortionAmount) + let pointX = center.x + cos(angle) * blobRadius + let pointY = center.y + sin(angle) * blobRadius + return CGPoint(x: pointX, y: pointY) + } + + // Use cubic Bézier curves for smoothness + var path = Path() + guard points.count > 2 else { return path } + + path.move(to: points[0]) + + // Calculate control points for smooth cubic Bézier curves + let count = points.count + for i in 0.. [UnitPoint] { + var points = [UnitPoint]() + let angleStep = 1.0 / CGFloat(count) + + for i in 0.. some View { + content + .background(material) // Use the specified material for the frosted glass base + .cornerRadius(radius) // Rounds the corners + .overlay( + // Adds subtle gradient border for dimensional effect + RoundedRectangle(cornerRadius: radius) + .stroke( + LinearGradient( + gradient: Gradient(colors: gradientColors()), + startPoint: .topLeading, + endPoint: .bottomTrailing + ), + lineWidth: strokeWidth + ) + ) + // Adds shadow for depth and elevation + .shadow(color: shadowColor.opacity(shadowOpacity), radius: shadowRadius, x: shadowX, y: shadowY) + } + + /// Generates the gradient colors based on the selected style + /// Creates the illusion of light reflection on glass edges + private func gradientColors() -> [Color] { + switch gradientStyle { + case .normal: + return [ + color.opacity(gradientOpacity), + .clear, + .clear, + color.opacity(gradientOpacity) + ] + case .reverted: + return [ + .clear, + color.opacity(gradientOpacity), + color.opacity(gradientOpacity), + .clear + ] + } + } +} diff --git a/StikJIT/SwiftGlass/SwiftGlass.swift b/StikJIT/SwiftGlass/SwiftGlass.swift new file mode 100644 index 00000000..ce7b96df --- /dev/null +++ b/StikJIT/SwiftGlass/SwiftGlass.swift @@ -0,0 +1,107 @@ +// +// SwiftGlass.swift +// SwiftGlass +// +// Created by Ming on 20/4/2025. +// +// CONTRIBUTING: +// ------------- +// - To report bugs or request features, please open an issue on GitHub +// - When submitting a pull request, please follow the coding style of the project +// +// CODE OVERVIEW: +// ------------- +// SwiftGlass provides a consistent glass/frosted effect across Apple platforms. +// The implementation consists of: +// 1. A View extension that applies the glass effect with customizable parameters +// 2. A ViewModifier that handles the actual rendering using materials, gradients, and shadows +// 3. Platform-specific handling (native API for visionOS, custom implementation elsewhere) +// + +import SwiftUI + +@available(iOS 15.0, macOS 14.0, watchOS 10.0, tvOS 15.0, visionOS 1.0, *) +public extension View { + /// Applies a customizable glass/frosted effect to the view + /// - Parameters: + /// - displayMode: Controls when the effect is displayed (.always or .automatic) + /// - radius: Corner radius of the glass effect + /// - color: Base color for the effect's gradient and highlights + /// - material: The material style to use for the glass effect + /// - gradientOpacity: Opacity level for the gradient overlay + /// - gradientStyle: Direction style of the gradient (.normal or .reverted) + /// - strokeWidth: Width of the border stroke + /// - shadowColor: Color of the drop shadow + /// - shadowOpacity: Opacity level for the shadow + /// - shadowRadius: Blur radius for the shadow (defaults to corner radius if nil) + /// - shadowX: Horizontal offset of the shadow + /// - shadowY: Vertical offset of the shadow + /// - Returns: A view with the glass effect applied + func glass( + displayMode: GlassBackgroundModifier.GlassBackgroundDisplayMode = .always, + radius: CGFloat = 32, + color: Color = .white, + material: Material = .ultraThinMaterial, + gradientOpacity: Double = 0.5, + gradientStyle: GlassBackgroundModifier.GradientStyle = .normal, + strokeWidth: CGFloat = 1.5, + shadowColor: Color = .white, + shadowOpacity: Double = 0.5, + shadowRadius: CGFloat? = nil, + shadowX: CGFloat = 0, + shadowY: CGFloat = 5 + ) -> some View { + #if os(visionOS) + // Use the native glass effect on visionOS for optimal rendering and system integration + return self.glassBackgroundEffect() + #else + // Custom implementation on other platforms combines materials, gradients and shadows + return modifier(GlassBackgroundModifier( + displayMode: displayMode, + radius: radius, + color: color, + material: material, + gradientOpacity: gradientOpacity, + gradientStyle: gradientStyle, + strokeWidth: strokeWidth, + shadowColor: shadowColor, + shadowOpacity: shadowOpacity, + shadowRadius: shadowRadius, + shadowX: shadowX, + shadowY: shadowY + )) + #endif + } + + /// Applies a liquefied glass effect with animation to the view + /// - Parameters: + /// - color: Base color for the jelly effect + /// - blobIntensity: Controls how pronounced the blob deformation is (0.0-1.0) + /// - animationSpeed: Controls the speed of the animation (1.0 is default) + /// - complexity: Number of control points that define the blob shape (4-12 recommended) + /// - material: The material style to use for the glass effect + /// - gradientOpacity: Opacity level for the gradient overlay + /// - shadowOpacity: Opacity level for the shadow + /// - Returns: A view with animated jelly glass effect applied + func liquefiedGlass( + color: Color = .blue, + blobIntensity: CGFloat = 0.5, + animationSpeed: Double = 1.0, + complexity: Int = 8, + material: Material = .ultraThinMaterial, + gradientOpacity: Double = 0.5, + shadowOpacity: Double = 0.3 + ) -> some View { + let modifier = BlobGlassModifier( + color: color, + blobIntensity: blobIntensity, + animationSpeed: animationSpeed, + complexity: complexity, + material: material, + gradientOpacity: gradientOpacity, + shadowOpacity: shadowOpacity + ) + + return self.modifier(modifier) + } +} diff --git a/StikJIT/Views/ConsoleLogsView.swift b/StikJIT/Views/ConsoleLogsView.swift index fbea02b3..72cd66db 100644 --- a/StikJIT/Views/ConsoleLogsView.swift +++ b/StikJIT/Views/ConsoleLogsView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import SwiftGlass import UIKit struct ConsoleLogsView: View { @@ -46,7 +47,7 @@ struct ConsoleLogsView: View { ZStack { if appTheme == "vision" { Color.clear - .background(.ultraThinMaterial) + .glass(radius: 0) .edgesIgnoringSafeArea(.all) } else { // Use system background color instead of fixed black diff --git a/StikJIT/Views/DisplayView.swift b/StikJIT/Views/DisplayView.swift index 4922bafc..a9df51af 100644 --- a/StikJIT/Views/DisplayView.swift +++ b/StikJIT/Views/DisplayView.swift @@ -4,6 +4,7 @@ // Created by neoarz on 4/9/25. import SwiftUI +import SwiftGlass struct AccentColorPicker: View { @Binding var selectedColor: Color @@ -79,7 +80,7 @@ struct DisplayView: View { ZStack { if appTheme == "vision" { Color.clear - .background(.ultraThinMaterial) + .glass(radius: 0) .ignoresSafeArea() } else { Color(colorScheme == .dark ? .black : UIColor.systemBackground) @@ -155,7 +156,7 @@ struct DisplayView: View { .background( Group { if appTheme == "vision" { - Color.clear.background(.ultraThinMaterial) + Color.clear.glass(radius: 16) } else { Color(UIColor.systemGray6) } @@ -193,7 +194,7 @@ struct DisplayView: View { .background( Group { if appTheme == "vision" { - Color.clear.background(.ultraThinMaterial) + Color.clear.glass(radius: 16) } else { Color(UIColor.systemGray6) } @@ -223,7 +224,7 @@ struct DisplayView: View { .background( Group { if appTheme == "vision" { - Color.clear.background(.ultraThinMaterial) + Color.clear.glass(radius: 16) } else { Color(UIColor.systemGray6) } diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index f77f42e4..42ad1c0c 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import SwiftGlass import UniformTypeIdentifiers extension UIDocumentPickerViewController { @@ -53,7 +54,7 @@ struct HomeView: View { ZStack { if appTheme == "vision" { Color.clear - .background(.ultraThinMaterial) + .glass(radius: 0) .edgesIgnoringSafeArea(.all) } else { // Use system background diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift index a35e3dbd..552d9395 100644 --- a/StikJIT/Views/SettingsView.swift +++ b/StikJIT/Views/SettingsView.swift @@ -4,6 +4,7 @@ // Created by Stephen on 3/27/25. import SwiftUI +import SwiftGlass import UniformTypeIdentifiers import UIKit @@ -61,7 +62,7 @@ struct SettingsView: View { ZStack { if appTheme == "vision" { Color.clear - .background(.ultraThinMaterial) + .glass(radius: 0) .ignoresSafeArea() } else { Color(UIColor.systemBackground) @@ -636,7 +637,7 @@ struct SettingsCard: View { .background( Group { if appTheme == "vision" { - Color.clear.background(.ultraThinMaterial) + Color.clear.glass(radius: 16) } else { Color(UIColor.secondarySystemBackground) }