diff --git a/TetheringHider.xcodeproj/project.pbxproj b/TetheringHider.xcodeproj/project.pbxproj index 494db1c..6583a61 100644 --- a/TetheringHider.xcodeproj/project.pbxproj +++ b/TetheringHider.xcodeproj/project.pbxproj @@ -7,12 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 0ED897B912F95BB51D023D21 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BC8C459FAEBF6E4430F356C /* ContentView.swift */; }; - 1996632E29C85D523F161D27 /* GlassModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A1E4DD25148C4B779DD7AB /* GlassModifiers.swift */; }; - 21C59AA87880BE42C84FA5AF /* StatusIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD85A3510C05A9BCF2AE3967 /* StatusIndicator.swift */; }; 26F71842BA0787F021CAA6B2 /* SystemCommandService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F78DC8BF18792636E9816D1 /* SystemCommandService.swift */; }; 2771ADA7302CEF6100A74E0C /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 2771ADA6302CEF6100A74E0C /* Localizable.xcstrings */; }; - 46B0F9471E81979738AF3B77 /* TTLControlPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F622A688DC3081B00C57AF0 /* TTLControlPanel.swift */; }; + 27B058A1302E147A00DC87F2 /* appicon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 27B058A0302E147A00DC87F2 /* appicon.icon */; }; C2D263A68C7C3715C72D43E0 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303AC637A762389B92B1789E /* AuthenticationService.swift */; }; C6FE29A2579CCD7D90FD002A /* TetheringHiderApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BF970E56192546A80A72095 /* TetheringHiderApp.swift */; }; F85087C3ACC19F8A9811FF55 /* TTLManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10849CA4224D433EE2901B17 /* TTLManager.swift */; }; @@ -20,15 +17,12 @@ /* Begin PBXFileReference section */ 10849CA4224D433EE2901B17 /* TTLManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTLManager.swift; sourceTree = ""; }; - 1F622A688DC3081B00C57AF0 /* TTLControlPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TTLControlPanel.swift; sourceTree = ""; }; 2771ADA6302CEF6100A74E0C /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + 27B058A0302E147A00DC87F2 /* appicon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = appicon.icon; sourceTree = ""; }; 2BF970E56192546A80A72095 /* TetheringHiderApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TetheringHiderApp.swift; sourceTree = ""; }; 303AC637A762389B92B1789E /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = ""; }; 7F78DC8BF18792636E9816D1 /* SystemCommandService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemCommandService.swift; sourceTree = ""; }; - 8BC8C459FAEBF6E4430F356C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; A8C3A702F0D5E0791E68D310 /* TetheringHider.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TetheringHider.app; sourceTree = BUILT_PRODUCTS_DIR; }; - E8A1E4DD25148C4B779DD7AB /* GlassModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlassModifiers.swift; sourceTree = ""; }; - FD85A3510C05A9BCF2AE3967 /* StatusIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusIndicator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXGroup section */ @@ -60,11 +54,7 @@ BF87D2EA1A464B929DA82A81 /* Views */ = { isa = PBXGroup; children = ( - 8BC8C459FAEBF6E4430F356C /* ContentView.swift */, 2771ADA6302CEF6100A74E0C /* Localizable.xcstrings */, - E8A1E4DD25148C4B779DD7AB /* GlassModifiers.swift */, - FD85A3510C05A9BCF2AE3967 /* StatusIndicator.swift */, - 1F622A688DC3081B00C57AF0 /* TTLControlPanel.swift */, ); path = Views; sourceTree = ""; @@ -72,6 +62,7 @@ C0FBE1EA54AD5144BFFD3461 = { isa = PBXGroup; children = ( + 27B058A0302E147A00DC87F2 /* appicon.icon */, D61DD9130F3129C572A0794F /* TetheringHider */, B96BAE44BD18AD7208D3DA80 /* Products */, ); @@ -116,7 +107,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 2660; + LastUpgradeCheck = 1600; TargetAttributes = { 522E0329F51D9AE0DD4F79C2 = { ProvisioningStyle = Manual; @@ -149,6 +140,7 @@ buildActionMask = 2147483647; files = ( 2771ADA7302CEF6100A74E0C /* Localizable.xcstrings in Resources */, + 27B058A1302E147A00DC87F2 /* appicon.icon in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -160,11 +152,7 @@ buildActionMask = 2147483647; files = ( C2D263A68C7C3715C72D43E0 /* AuthenticationService.swift in Sources */, - 0ED897B912F95BB51D023D21 /* ContentView.swift in Sources */, - 1996632E29C85D523F161D27 /* GlassModifiers.swift in Sources */, - 21C59AA87880BE42C84FA5AF /* StatusIndicator.swift in Sources */, 26F71842BA0787F021CAA6B2 /* SystemCommandService.swift in Sources */, - 46B0F9471E81979738AF3B77 /* TTLControlPanel.swift in Sources */, F85087C3ACC19F8A9811FF55 /* TTLManager.swift in Sources */, C6FE29A2579CCD7D90FD002A /* TetheringHiderApp.swift in Sources */, ); @@ -177,8 +165,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = appicon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -211,12 +198,10 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_APP_SANDBOX = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -230,7 +215,6 @@ MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; @@ -241,16 +225,16 @@ 31E4FAB6483D842FA3A3FCDE /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = appicon; CODE_SIGN_ENTITLEMENTS = TetheringHider/TetheringHider.entitlements; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_APP_SANDBOX = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDevelopmentRegion = es; INFOPLIST_KEY_CFBundleDisplayName = "Tethering Hider"; + INFOPLIST_KEY_CFBundleIconName = appicon; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( @@ -269,16 +253,16 @@ 7EADBBF3285DFAA6D9F296D1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_APPICON_NAME = appicon; CODE_SIGN_ENTITLEMENTS = TetheringHider/TetheringHider.entitlements; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_APP_SANDBOX = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_CFBundleDevelopmentRegion = es; INFOPLIST_KEY_CFBundleDisplayName = "Tethering Hider"; + INFOPLIST_KEY_CFBundleIconName = appicon; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( @@ -298,8 +282,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = appicon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -332,12 +315,10 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_APP_SANDBOX = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -358,7 +339,6 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; - STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; diff --git a/TetheringHider/Services/SystemCommandService.swift b/TetheringHider/Services/SystemCommandService.swift index 6502c00..5d0340d 100644 --- a/TetheringHider/Services/SystemCommandService.swift +++ b/TetheringHider/Services/SystemCommandService.swift @@ -1,3 +1,10 @@ +// +// TetheringHiderApp.swift +// +// +// Created by Jesús David Chapman Vélez on 12/08/26. +// + import Foundation enum SystemCommandError: LocalizedError { diff --git a/TetheringHider/TetheringHiderApp.swift b/TetheringHider/TetheringHiderApp.swift index 00a56e8..ed58dc0 100644 --- a/TetheringHider/TetheringHiderApp.swift +++ b/TetheringHider/TetheringHiderApp.swift @@ -1,10 +1,3 @@ -// -// TetheringHiderApp.swift -// -// -// Created by Jesús David Chapman Vélez on 12/08/26. -// - import SwiftUI import AppKit @@ -45,6 +38,7 @@ final class StatusBarController: NSObject, NSMenuDelegate { let menu = NSMenu() menu.delegate = self + // 1. Status Header with Cloak/Visible Icon let statusTitle = ttlManager.isProtected ? String(localized: "Tethering Hider — Hotspot Cloaked") : String(localized: "Tethering Hider — Standard Tethering") @@ -59,6 +53,7 @@ final class StatusBarController: NSObject, NSMenuDelegate { menu.addItem(NSMenuItem.separator()) + // 2. Toggle Action with Cloak/Uncloak Icon let toggleTitle = ttlManager.isProtected ? String(localized: "Disable Hotspot Cloak (Set TTL 64)") : String(localized: "Enable Hotspot Cloak (Set TTL 65)") @@ -73,6 +68,7 @@ final class StatusBarController: NSObject, NSMenuDelegate { menu.addItem(NSMenuItem.separator()) + // 3. Info Items with Network & DNS Icons let ipv4Format = String(localized: "IPv4 TTL: %d") let ipv4Item = NSMenuItem(title: String(format: ipv4Format, ttlManager.currentTTL), action: nil, keyEquivalent: "") ipv4Item.isEnabled = false @@ -93,6 +89,7 @@ final class StatusBarController: NSObject, NSMenuDelegate { menu.addItem(NSMenuItem.separator()) + // 4. About Panel Option let aboutTitle = String(localized: "About Tethering Hider...") let aboutItem = NSMenuItem(title: aboutTitle, action: #selector(aboutAction), keyEquivalent: "") aboutItem.target = self @@ -101,6 +98,7 @@ final class StatusBarController: NSObject, NSMenuDelegate { menu.addItem(NSMenuItem.separator()) + // 5. Quit Option with Standard macOS Exit Icon let quitTitle = String(localized: "Quit Tethering Hider") let quitItem = NSMenuItem(title: quitTitle, action: #selector(quitAction), keyEquivalent: "q") quitItem.target = self @@ -136,7 +134,7 @@ final class StatusBarController: NSObject, NSMenuDelegate { @objc private func aboutAction() { let creditsString = String(localized: "Bypasses mobile carrier hotspot tethering detection by adjusting outgoing IPv4 TTL to 65, IPv6 Hop Limit to 65, and flushing system DNS caches.\n\nDesigned for macOS.") - let options: [NSApplication.AboutPanelOptionKey: Any] = [ + var options: [NSApplication.AboutPanelOptionKey: Any] = [ .credits: NSAttributedString( string: creditsString, attributes: [ @@ -147,6 +145,11 @@ final class StatusBarController: NSObject, NSMenuDelegate { .applicationName: "Tethering Hider", .version: "1.0.0" ] + + if let appIcon = NSImage(named: NSImage.applicationIconName) ?? NSApplication.shared.applicationIconImage { + options[.applicationIcon] = appIcon + } + NSApp.orderFrontStandardAboutPanel(options: options) NSApp.activate(ignoringOtherApps: true) } diff --git a/TetheringHider/Views/ContentView.swift b/TetheringHider/Views/ContentView.swift deleted file mode 100644 index df6d8a8..0000000 --- a/TetheringHider/Views/ContentView.swift +++ /dev/null @@ -1,218 +0,0 @@ -// -// ContentView.swift -// -// -// Created by Jesús David Chapman Vélez on 12/08/26. -// - -import SwiftUI - -struct ContentView: View { - @Environment(TTLManager.self) private var ttlManager - @AppStorage("startMinimized") private var startMinimized = false - @State private var hasHandledInitialLaunch = false - @State private var isProcessing = false - @State private var showError = false - @State private var errorMessage = "" - @Namespace private var shieldNamespace - - private let authService = AuthenticationService() - private let commandService = SystemCommandService() - - @Environment(\.colorScheme) private var colorScheme - - private var accentColor: Color { - if colorScheme == .light { - return ttlManager.isProtected - ? Color(hue: 0.38, saturation: 0.85, brightness: 0.40) - : Color(hue: 0.05, saturation: 0.90, brightness: 0.45) - } else { - return ttlManager.isProtected - ? Color(hue: 0.38, saturation: 0.70, brightness: 0.85) - : Color(hue: 0.06, saturation: 0.70, brightness: 0.90) - } - } - - var body: some View { - ZStack { - // Background gradient - backgroundGradient - - VStack(spacing: 0) { - // Header with glass - headerSection - .padding(.horizontal, 20) - .padding(.vertical, 10) - .modifier(GlassInteractiveModifier(cornerRadius: 14, tintColor: accentColor)) - .padding(.top, 14) - - Spacer(minLength: 12) - - // Central antenna indicator - StatusIndicator( - isProtected: ttlManager.isProtected, - isProcessing: isProcessing - ) - .padding(.vertical, 10) - - Spacer(minLength: 12) - - // Control panel - TTLControlPanel( - currentTTL: ttlManager.currentTTL, - currentIPv6HopLimit: ttlManager.currentIPv6HopLimit, - targetTTL: ttlManager.targetTTL, - defaultTTL: ttlManager.defaultTTL, - isProtected: ttlManager.isProtected, - isProcessing: isProcessing, - onActivate: activateProtection, - onDeactivate: deactivateProtection - ) - .padding(.horizontal, 24) - .padding(.bottom, 24) - } - } - .frame(width: 400, height: 580) - .alert("Error", isPresented: $showError) { - Button("OK", role: .cancel) { } - } message: { - Text(errorMessage) - } - .task { - ttlManager.refreshStatus() - } - } - - private var backgroundGradient: some View { - MeshGradient( - width: 3, height: 3, - points: [ - [0.0, 0.0], [0.5, 0.0], [1.0, 0.0], - [0.0, 0.5], [0.5, 0.5], [1.0, 0.5], - [0.0, 1.0], [0.5, 1.0], [1.0, 1.0] - ], - colors: meshColors - ) - .ignoresSafeArea() - .animation(.easeInOut(duration: 1.2), value: ttlManager.isProtected) - .animation(.easeInOut(duration: 0.5), value: colorScheme) - } - - private var meshColors: [Color] { - if colorScheme == .light { - return ttlManager.isProtected ? [ - Color(hue: 0.44, saturation: 0.22, brightness: 0.95), - Color(hue: 0.48, saturation: 0.18, brightness: 0.98), - Color(hue: 0.52, saturation: 0.22, brightness: 0.94), - Color(hue: 0.40, saturation: 0.16, brightness: 0.96), - Color(hue: 0.46, saturation: 0.26, brightness: 0.93), - Color(hue: 0.50, saturation: 0.18, brightness: 0.97), - Color(hue: 0.42, saturation: 0.20, brightness: 0.95), - Color(hue: 0.45, saturation: 0.16, brightness: 0.98), - Color(hue: 0.51, saturation: 0.22, brightness: 0.94) - ] : [ - Color(hue: 0.04, saturation: 0.18, brightness: 0.96), - Color(hue: 0.07, saturation: 0.14, brightness: 0.98), - Color(hue: 0.09, saturation: 0.18, brightness: 0.95), - Color(hue: 0.02, saturation: 0.14, brightness: 0.97), - Color(hue: 0.05, saturation: 0.24, brightness: 0.94), - Color(hue: 0.08, saturation: 0.16, brightness: 0.98), - Color(hue: 0.03, saturation: 0.18, brightness: 0.95), - Color(hue: 0.06, saturation: 0.14, brightness: 0.98), - Color(hue: 0.08, saturation: 0.18, brightness: 0.94) - ] - } else { - return ttlManager.isProtected ? [ - Color(hue: 0.45, saturation: 0.4, brightness: 0.08), - Color(hue: 0.50, saturation: 0.3, brightness: 0.10), - Color(hue: 0.55, saturation: 0.4, brightness: 0.08), - Color(hue: 0.40, saturation: 0.3, brightness: 0.12), - Color(hue: 0.48, saturation: 0.5, brightness: 0.14), - Color(hue: 0.52, saturation: 0.3, brightness: 0.10), - Color(hue: 0.42, saturation: 0.4, brightness: 0.06), - Color(hue: 0.47, saturation: 0.3, brightness: 0.10), - Color(hue: 0.53, saturation: 0.4, brightness: 0.08) - ] : [ - Color(hue: 0.02, saturation: 0.3, brightness: 0.08), - Color(hue: 0.05, saturation: 0.2, brightness: 0.10), - Color(hue: 0.08, saturation: 0.3, brightness: 0.08), - Color(hue: 0.00, saturation: 0.2, brightness: 0.12), - Color(hue: 0.04, saturation: 0.4, brightness: 0.14), - Color(hue: 0.07, saturation: 0.2, brightness: 0.10), - Color(hue: 0.01, saturation: 0.3, brightness: 0.06), - Color(hue: 0.03, saturation: 0.2, brightness: 0.10), - Color(hue: 0.06, saturation: 0.3, brightness: 0.08) - ] - } - } - - // MARK: - Header - - private var headerSection: some View { - VStack(spacing: 6) { - Text("TETHERING HIDER") - .font(.system(size: 14, weight: .bold, design: .rounded)) - .tracking(4) - .foregroundStyle(.secondary) - - Text(ttlManager.isProtected ? "Tethering Hidden" : "Tethering Exposed") - .font(.system(size: 13, weight: .medium)) - .foregroundStyle(ttlManager.isProtected - ? Color(hue: 0.38, saturation: 0.7, brightness: 0.85) - : Color(hue: 0.06, saturation: 0.7, brightness: 0.90) - ) - .contentTransition(.numericText()) - .animation(.easeInOut, value: ttlManager.isProtected) - } - } - - private func activateProtection() { - Task { - isProcessing = true - defer { isProcessing = false } - - do { - // Authenticate with Touch ID - let authenticated = try await authService.authenticateWithBiometrics() - guard authenticated else { return } - - // Execute privileged command - try await commandService.setTTL(ttlManager.targetTTL) - - // Refresh status - ttlManager.didSetTTL(ttlManager.targetTTL) - } catch is AuthenticationError { - - } catch SystemCommandError.userCancelledAuth { - - } catch { - errorMessage = error.localizedDescription - showError = true - ttlManager.setError(error.localizedDescription) - } - } - } - - private func deactivateProtection() { - Task { - isProcessing = true - defer { isProcessing = false } - - do { - let authenticated = try await authService.authenticateWithBiometrics() - guard authenticated else { return } - - try await commandService.resetTTL() - ttlManager.didSetTTL(ttlManager.defaultTTL) - } catch is AuthenticationError { - - } catch SystemCommandError.userCancelledAuth { - - } catch { - errorMessage = error.localizedDescription - showError = true - ttlManager.setError(error.localizedDescription) - } - } - } -} diff --git a/TetheringHider/Views/GlassModifiers.swift b/TetheringHider/Views/GlassModifiers.swift deleted file mode 100644 index 7742340..0000000 --- a/TetheringHider/Views/GlassModifiers.swift +++ /dev/null @@ -1,46 +0,0 @@ -import SwiftUI - -struct GlassCardModifier: ViewModifier { - let tintColor: Color - - func body(content: Content) -> some View { - if #available(macOS 26, *) { - content - .glassEffect(.regular.tint(tintColor.opacity(0.15)), in: .rect(cornerRadius: 16)) - } else { - content - } - } -} - -struct GlassButtonModifier: ViewModifier { - var isActive: Bool = false - - func body(content: Content) -> some View { - if #available(macOS 26, *) { - content - .buttonStyle(.glassProminent) - } else { - content - .buttonStyle(.borderedProminent) - } - } -} - -struct GlassInteractiveModifier: ViewModifier { - var cornerRadius: CGFloat = 20 - var tintColor: Color = .clear - - func body(content: Content) -> some View { - if #available(macOS 26, *) { - content - .glassEffect( - .regular.tint(tintColor.opacity(0.1)).interactive(), - in: .rect(cornerRadius: cornerRadius) - ) - } else { - content - .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: cornerRadius)) - } - } -} diff --git a/TetheringHider/Views/Localizable.xcstrings b/TetheringHider/Views/Localizable.xcstrings index 18a9c0f..5703758 100644 --- a/TetheringHider/Views/Localizable.xcstrings +++ b/TetheringHider/Views/Localizable.xcstrings @@ -1,22 +1,6 @@ { "sourceLanguage" : "en", "strings" : { - "" : { - - }, - "%@: %@" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "%1$@: %2$@" - } - } - } - }, - "%lld" : { - - }, "About Tethering Hider..." : { "localizations" : { "es" : { @@ -26,9 +10,6 @@ } } } - }, - "Activate Hider" : { - }, "Bypasses mobile carrier hotspot tethering detection by adjusting outgoing IPv4 TTL to 65, IPv6 Hop Limit to 65, and flushing system DNS caches.\n\nDesigned for macOS." : { "localizations" : { @@ -39,9 +20,6 @@ } } } - }, - "Deactivate Hider" : { - }, "Disable Hotspot Cloak (Set TTL 64)" : { "localizations" : { @@ -72,9 +50,6 @@ } } } - }, - "Error" : { - }, "IPv4 TTL: %d" : { "localizations" : { @@ -95,9 +70,6 @@ } } } - }, - "OK" : { - }, "Quit Tethering Hider" : { "localizations" : { @@ -108,18 +80,6 @@ } } } - }, - "Start minimized in menu bar" : { - - }, - "Tethering Exposed" : { - - }, - "Tethering Hidden" : { - - }, - "TETHERING HIDER" : { - }, "Tethering Hider — Hotspot Cloaked" : { "localizations" : { diff --git a/TetheringHider/Views/MenuBarView.swift b/TetheringHider/Views/MenuBarView.swift deleted file mode 100644 index 7f32484..0000000 --- a/TetheringHider/Views/MenuBarView.swift +++ /dev/null @@ -1,181 +0,0 @@ -import SwiftUI - -struct MenuBarView: View { - @Environment(TTLManager.self) private var ttlManager - @Environment(\.openWindow) private var openWindow - @Environment(\.colorScheme) private var colorScheme - - @State private var isProcessing = false - @State private var showError = false - @State private var errorMessage = "" - - private let authService = AuthenticationService() - private let commandService = SystemCommandService() - - private var accentColor: Color { - if colorScheme == .light { - return ttlManager.isProtected - ? Color(hue: 0.38, saturation: 0.85, brightness: 0.40) - : Color(hue: 0.05, saturation: 0.90, brightness: 0.45) - } else { - return ttlManager.isProtected - ? Color(hue: 0.38, saturation: 0.70, brightness: 0.85) - : Color(hue: 0.06, saturation: 0.70, brightness: 0.90) - } - } - - var body: some View { - VStack(spacing: 14) { - // Header - HStack(spacing: 10) { - Image(systemName: ttlManager.isProtected ? "antenna.radiowaves.left.and.right" : "antenna.radiowaves.left.and.right.slash") - .font(.system(size: 20, weight: .medium)) - .foregroundStyle(accentColor) - .symbolEffect(.bounce, value: ttlManager.isProtected) - - VStack(alignment: .leading, spacing: 2) { - Text("Tethering Hider") - .font(.system(size: 13, weight: .bold, design: .rounded)) - - Text(ttlManager.isProtected ? "Protected (TTL 65)" : "Exposed (TTL 64)") - .font(.system(size: 11, weight: .medium)) - .foregroundStyle(accentColor) - } - - Spacer() - - // Toggle Switch - Toggle("", isOn: Binding( - get: { ttlManager.isProtected }, - set: { newValue in - if newValue { - activateProtection() - } else { - deactivateProtection() - } - } - )) - .toggleStyle(.switch) - .disabled(isProcessing) - .tint(accentColor) - } - .padding(.horizontal, 14) - .padding(.vertical, 10) - .background( - RoundedRectangle(cornerRadius: 12) - .fill(.ultraThinMaterial) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(accentColor.opacity(0.2), lineWidth: 0.5) - ) - ) - .modifier(GlassCardModifier(tintColor: accentColor)) - - // Status Badges - HStack(spacing: 6) { - badgeItem(title: "IPv4", val: "\(ttlManager.currentTTL)", active: ttlManager.currentTTL == 65) - badgeItem(title: "IPv6", val: "\(ttlManager.currentIPv6HopLimit)", active: ttlManager.currentIPv6HopLimit == 65) - badgeItem(title: "DNS", val: "FLUSH", active: ttlManager.isProtected) - } - - Divider() - .opacity(0.3) - - // Action Links - VStack(spacing: 6) { - Button { - NSApp.activate(ignoringOtherApps: true) - openWindow(id: "mainWindow") - } label: { - Label("Open Dashboard", systemImage: "macwindow") - .font(.system(size: 12, weight: .medium)) - .frame(maxWidth: .infinity, alignment: .leading) - .contentShape(Rectangle()) - } - .buttonStyle(.plain) - .padding(.horizontal, 10) - .padding(.vertical, 6) - .background(RoundedRectangle(cornerRadius: 6).fill(Color.primary.opacity(0.05))) - - Button(role: .destructive) { - NSApp.terminate(nil) - } label: { - Label("Quit Tethering Hider", systemImage: "power") - .font(.system(size: 12, weight: .medium)) - .foregroundStyle(.red) - .frame(maxWidth: .infinity, alignment: .leading) - .contentShape(Rectangle()) - } - .buttonStyle(.plain) - .padding(.horizontal, 10) - .padding(.vertical, 6) - } - } - .padding(14) - .frame(width: 280) - .alert("Error", isPresented: $showError) { - Button("OK", role: .cancel) { } - } message: { - Text(errorMessage) - } - .task { - ttlManager.refreshStatus() - } - } - - private func badgeItem(title: String, val: String, active: Bool) -> some View { - HStack(spacing: 3) { - Circle() - .fill(active ? Color.green : Color.orange) - .frame(width: 4, height: 4) - Text("\(title): \(val)") - .font(.system(size: 9, weight: .medium, design: .monospaced)) - .foregroundStyle(.secondary) - } - .padding(.horizontal, 6) - .padding(.vertical, 3) - .background(Capsule().fill(.ultraThinMaterial)) - } - - private func activateProtection() { - Task { - isProcessing = true - defer { isProcessing = false } - - do { - let authenticated = try await authService.authenticateWithBiometrics() - guard authenticated else { return } - - try await commandService.setTTL(ttlManager.targetTTL) - ttlManager.didSetTTL(ttlManager.targetTTL) - } catch is AuthenticationError { - } catch SystemCommandError.userCancelledAuth { - } catch { - errorMessage = error.localizedDescription - showError = true - ttlManager.setError(error.localizedDescription) - } - } - } - - private func deactivateProtection() { - Task { - isProcessing = true - defer { isProcessing = false } - - do { - let authenticated = try await authService.authenticateWithBiometrics() - guard authenticated else { return } - - try await commandService.resetTTL() - ttlManager.didSetTTL(ttlManager.defaultTTL) - } catch is AuthenticationError { - } catch SystemCommandError.userCancelledAuth { - } catch { - errorMessage = error.localizedDescription - showError = true - ttlManager.setError(error.localizedDescription) - } - } - } -} diff --git a/TetheringHider/Views/StatusIndicator.swift b/TetheringHider/Views/StatusIndicator.swift deleted file mode 100644 index 0c13ab0..0000000 --- a/TetheringHider/Views/StatusIndicator.swift +++ /dev/null @@ -1,110 +0,0 @@ -import SwiftUI - -struct StatusIndicator: View { - let isProtected: Bool - let isProcessing: Bool - - @Environment(\.colorScheme) private var colorScheme - @State private var pulseScale: CGFloat = 1.0 - @State private var glowOpacity: Double = 0.4 - @State private var rotationAngle: Double = 0 - - private var accentColor: Color { - if colorScheme == .light { - return isProtected - ? Color(hue: 0.38, saturation: 0.85, brightness: 0.40) - : Color(hue: 0.05, saturation: 0.90, brightness: 0.45) - } else { - return isProtected - ? Color(hue: 0.38, saturation: 0.75, brightness: 0.80) - : Color(hue: 0.06, saturation: 0.75, brightness: 0.85) - } - } - - private var statusText: String { - if isProcessing { return "Processing..." } - return isProtected ? "PROTECTED" : "EXPOSED" - } - - var body: some View { - VStack(spacing: 20) { - ZStack { - - ForEach(0..<3, id: \.self) { ring in - Circle() - .stroke( - accentColor.opacity(colorScheme == .light ? (0.28 - Double(ring) * 0.07) : (0.15 - Double(ring) * 0.04)), - lineWidth: colorScheme == .light ? 2.0 : 1.5 - ) - .frame( - width: 140 + CGFloat(ring) * 22, - height: 140 + CGFloat(ring) * 22 - ) - .scaleEffect(pulseScale + CGFloat(ring) * 0.02) - } - - Circle() - .fill( - RadialGradient( - colors: [ - accentColor.opacity(0.25), - accentColor.opacity(0.08), - .clear - ], - center: .center, - startRadius: 20, - endRadius: 75 - ) - ) - .frame(width: 130, height: 130) - .scaleEffect(pulseScale) - - // Antenna icon - Image(systemName: isProtected ? "antenna.radiowaves.left.and.right" : "antenna.radiowaves.left.and.right.slash") - .font(.system(size: 52, weight: .light)) - .foregroundStyle(accentColor) - .symbolEffect(.bounce, value: isProtected) - .contentTransition(.symbolEffect(.replace.magic(fallback: .replace))) - .shadow(color: accentColor.opacity(0.5), radius: 16) - - // Processing spinner - if isProcessing { - Circle() - .trim(from: 0, to: 0.7) - .stroke( - accentColor, - style: StrokeStyle(lineWidth: 2, lineCap: .round) - ) - .frame(width: 125, height: 125) - .rotationEffect(.degrees(rotationAngle)) - } - } - .animation(.easeInOut(duration: 0.8), value: isProtected) - .animation(.easeInOut(duration: 0.4), value: isProcessing) - - // Status text - Text(statusText) - .font(.system(size: 16, weight: .semibold, design: .rounded)) - .tracking(3) - .foregroundStyle(accentColor) - .contentTransition(.numericText()) - .animation(.easeInOut, value: statusText) - } - .onAppear { - withAnimation( - .easeInOut(duration: 2.0) - .repeatForever(autoreverses: true) - ) { - pulseScale = 1.04 - glowOpacity = 0.7 - } - - withAnimation( - .linear(duration: 1.0) - .repeatForever(autoreverses: false) - ) { - rotationAngle = 360 - } - } - } -} diff --git a/TetheringHider/Views/TTLControlPanel.swift b/TetheringHider/Views/TTLControlPanel.swift deleted file mode 100644 index f8b8c62..0000000 --- a/TetheringHider/Views/TTLControlPanel.swift +++ /dev/null @@ -1,207 +0,0 @@ -import SwiftUI - -struct TTLControlPanel: View { - let currentTTL: Int - let currentIPv6HopLimit: Int - let targetTTL: Int - let defaultTTL: Int - let isProtected: Bool - let isProcessing: Bool - let onActivate: () -> Void - let onDeactivate: () -> Void - - @AppStorage("startMinimized") private var startMinimized = false - @Environment(\.colorScheme) private var colorScheme - @Namespace private var controlNamespace - - @State private var isHovering = false - - private var accentColor: Color { - if colorScheme == .light { - return isProtected - ? Color(hue: 0.38, saturation: 0.85, brightness: 0.40) - : Color(hue: 0.05, saturation: 0.90, brightness: 0.45) - } else { - return isProtected - ? Color(hue: 0.38, saturation: 0.70, brightness: 0.80) - : Color(hue: 0.06, saturation: 0.70, brightness: 0.85) - } - } - - private var buttonTint: Color { - if colorScheme == .light { - return isProtected - ? Color(hue: 0.05, saturation: 0.85, brightness: isHovering ? 0.60 : 0.50) - : Color(hue: 0.38, saturation: 0.85, brightness: isHovering ? 0.50 : 0.42) - } else { - return isProtected - ? Color(hue: 0.06, saturation: 0.65, brightness: isHovering ? 0.90 : 0.80) - : Color(hue: 0.38, saturation: 0.65, brightness: isHovering ? 0.90 : 0.80) - } - } - - var body: some View { - VStack(spacing: 12) { - ttlInfoCard - protocolBadges - actionButton - settingsCard - } - } - - private var ttlInfoCard: some View { - HStack(spacing: 20) { - ttlValueDisplay( - label: "CURRENT", - value: currentTTL, - highlight: isProtected - ) - - Image(systemName: "arrow.right") - .font(.system(size: 14, weight: .medium)) - .foregroundStyle(.tertiary) - - ttlValueDisplay( - label: "TARGET", - value: targetTTL, - highlight: true - ) - } - .padding(.horizontal, 24) - .padding(.vertical, 12) - .frame(maxWidth: .infinity) - .background { - RoundedRectangle(cornerRadius: 16) - .fill(.ultraThinMaterial) - .overlay { - RoundedRectangle(cornerRadius: 16) - .stroke(accentColor.opacity(0.15), lineWidth: 0.5) - } - } - .modifier(GlassCardModifier(tintColor: accentColor)) - } - - private func ttlValueDisplay(label: String, value: Int, highlight: Bool) -> some View { - VStack(spacing: 4) { - Text(label) - .font(.system(size: 10, weight: .semibold, design: .rounded)) - .tracking(2) - .foregroundStyle(.secondary) - - Text("\(value)") - .font(.system(size: 30, weight: .bold, design: .monospaced)) - .foregroundStyle(highlight ? accentColor : .primary) - .contentTransition(.numericText()) - .animation(.spring(duration: 0.5), value: value) - } - } - - // Protocol Badges - - private var protocolBadges: some View { - HStack(spacing: 8) { - badgeView(title: "IPv4 TTL", value: "\(currentTTL)", active: currentTTL == targetTTL) - badgeView(title: "IPv6 HLIM", value: "\(currentIPv6HopLimit)", active: currentIPv6HopLimit == targetTTL) - badgeView(title: "DNS FLUSH", value: "AUTO", active: isProtected) - } - } - - private func badgeView(title: String, value: String, active: Bool) -> some View { - HStack(spacing: 4) { - Circle() - .fill(active ? Color.green : Color.orange) - .frame(width: 5, height: 5) - - Text("\(title): \(value)") - .font(.system(size: 9, weight: .medium, design: .monospaced)) - .foregroundStyle(.secondary) - } - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background( - Capsule() - .fill(.ultraThinMaterial) - .overlay( - Capsule().stroke(active ? Color.green.opacity(0.3) : Color.orange.opacity(0.2), lineWidth: 0.5) - ) - ) - } - - // Action Button - - private var actionButton: some View { - Button { - if isProtected { - onDeactivate() - } else { - onActivate() - } - } label: { - HStack(spacing: 10) { - if isProcessing { - ProgressView() - .controlSize(.small) - .tint(.white) - } else { - Image(systemName: isProtected ? "antenna.radiowaves.left.and.right.slash" : "antenna.radiowaves.left.and.right") - .font(.system(size: 16, weight: .semibold)) - .symbolEffect(.bounce, value: isHovering) - .contentTransition(.symbolEffect(.replace.magic(fallback: .replace))) - } - - Text(isProtected ? "Deactivate Hider" : "Activate Hider") - .font(.system(size: 15, weight: .semibold, design: .rounded)) - .contentTransition(.numericText()) - } - .frame(maxWidth: .infinity) - .padding(.vertical, 12) - } - .modifier(GlassButtonModifier()) - .tint(buttonTint) - .scaleEffect(isHovering ? 1.025 : 1.0) - .shadow( - color: (isProtected - ? Color(hue: 0.06, saturation: 0.8, brightness: colorScheme == .light ? 0.5 : 0.9) - : Color(hue: 0.38, saturation: 0.8, brightness: colorScheme == .light ? 0.4 : 0.9) - ).opacity(isHovering ? (colorScheme == .light ? 0.35 : 0.45) : 0.0), - radius: isHovering ? 12 : 0, - x: 0, - y: isHovering ? 4 : 0 - ) - .onHover { hovering in - withAnimation(.spring(response: 0.35, dampingFraction: 0.7)) { - isHovering = hovering - } - } - .disabled(isProcessing) - .animation(.easeInOut(duration: 0.3), value: isProtected) - .animation(.easeInOut(duration: 0.3), value: isProcessing) - } - - // Settings Card - - private var settingsCard: some View { - HStack { - Label("Start minimized in menu bar", systemImage: "macwindow.on.rectangle") - .font(.system(size: 11, weight: .medium)) - .foregroundStyle(.secondary) - - Spacer() - - Toggle("", isOn: $startMinimized) - .toggleStyle(.switch) - .controlSize(.small) - .tint(accentColor) - } - .padding(.horizontal, 12) - .padding(.vertical, 7) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(.ultraThinMaterial) - .overlay( - RoundedRectangle(cornerRadius: 10) - .stroke(accentColor.opacity(0.12), lineWidth: 0.5) - ) - ) - } -} diff --git a/appicon.icon/Assets/tethering-hidder.png b/appicon.icon/Assets/tethering-hidder.png new file mode 100644 index 0000000..bcd42fe Binary files /dev/null and b/appicon.icon/Assets/tethering-hidder.png differ diff --git a/appicon.icon/icon.json b/appicon.icon/icon.json new file mode 100644 index 0000000..d4ecfd3 --- /dev/null +++ b/appicon.icon/icon.json @@ -0,0 +1,51 @@ +{ + "fill" : { + "linear-gradient" : [ + "srgb:0.92143,0.92145,0.92144,1.00000", + "srgb:0.92143,0.92145,0.92144,1.00000" + ], + "orientation" : { + "start" : { + "x" : 0.5, + "y" : 0 + }, + "stop" : { + "x" : 0.5, + "y" : 0.7 + } + } + }, + "groups" : [ + { + "layers" : [ + { + "fill" : "automatic", + "image-name" : "tethering-hidder.png", + "name" : "tethering-hidder", + "opacity" : 0.8, + "position" : { + "scale" : 1, + "translation-in-points" : [ + 16.71898342510258, + 9.266469244762675 + ] + } + } + ], + "shadow" : { + "kind" : "neutral", + "opacity" : 0.5 + }, + "translucency" : { + "enabled" : true, + "value" : 0.5 + } + } + ], + "supported-platforms" : { + "circles" : [ + "watchOS" + ], + "squares" : "shared" + } +} \ No newline at end of file