2026-08-12 23:44:59 -05:00
import SwiftUI
import AppKit
@ MainActor
final class StatusBarController : NSObject , NSMenuDelegate {
private var statusItem : NSStatusItem ?
private let ttlManager : TTLManager
private let authService = AuthenticationService ( )
private let commandService = SystemCommandService ( )
init ( ttlManager : TTLManager ) {
self . ttlManager = ttlManager
super . init ( )
setupStatusItem ( )
self . ttlManager . onStatusChanged = { [ weak self ] in
Task { @ MainActor in
self ? . updateMenu ( )
}
}
}
func setupStatusItem ( ) {
statusItem = NSStatusBar . system . statusItem ( withLength : NSStatusItem . variableLength )
updateMenu ( )
}
func updateMenu ( ) {
guard let statusItem = statusItem , let button = statusItem . button else { return }
// S t a t u s B a r I c o n
let imageName = ttlManager . isProtected
? " antenna.radiowaves.left.and.right "
: " antenna.radiowaves.left.and.right.slash "
button . image = NSImage ( systemSymbolName : imageName , accessibilityDescription : " Tethering Hider " )
let menu = NSMenu ( )
menu . delegate = self
2026-08-13 10:35:10 -05:00
// 1 . S t a t u s H e a d e r w i t h C l o a k / V i s i b l e I c o n
2026-08-12 23:44:59 -05:00
let statusTitle = ttlManager . isProtected
? String ( localized : " Tethering Hider — Hotspot Cloaked " )
: String ( localized : " Tethering Hider — Standard Tethering " )
let headerItem = NSMenuItem ( title : statusTitle , action : nil , keyEquivalent : " " )
headerItem . isEnabled = false
headerItem . image = NSImage (
systemSymbolName : ttlManager . isProtected ? " eye.slash.fill " : " eye.fill " ,
accessibilityDescription : nil
)
menu . addItem ( headerItem )
menu . addItem ( NSMenuItem . separator ( ) )
2026-08-13 10:35:10 -05:00
// 2 . T o g g l e A c t i o n w i t h C l o a k / U n c l o a k I c o n
2026-08-12 23:44:59 -05:00
let toggleTitle = ttlManager . isProtected
? String ( localized : " Disable Hotspot Cloak (Set TTL 64) " )
: String ( localized : " Enable Hotspot Cloak (Set TTL 65) " )
let toggleItem = NSMenuItem ( title : toggleTitle , action : #selector ( toggleAction ) , keyEquivalent : " " )
toggleItem . target = self
toggleItem . image = NSImage (
systemSymbolName : ttlManager . isProtected ? " eye.fill " : " eye.slash.fill " ,
accessibilityDescription : nil
)
menu . addItem ( toggleItem )
menu . addItem ( NSMenuItem . separator ( ) )
2026-08-13 10:35:10 -05:00
// 3 . I n f o I t e m s w i t h N e t w o r k & D N S I c o n s
2026-08-12 23:44:59 -05:00
let ipv4Format = String ( localized : " IPv4 TTL: %d " )
let ipv4Item = NSMenuItem ( title : String ( format : ipv4Format , ttlManager . currentTTL ) , action : nil , keyEquivalent : " " )
ipv4Item . isEnabled = false
ipv4Item . image = NSImage ( systemSymbolName : " network " , accessibilityDescription : nil )
menu . addItem ( ipv4Item )
let ipv6Format = String ( localized : " IPv6 HLIM: %d " )
let ipv6Item = NSMenuItem ( title : String ( format : ipv6Format , ttlManager . currentIPv6HopLimit ) , action : nil , keyEquivalent : " " )
ipv6Item . isEnabled = false
ipv6Item . image = NSImage ( systemSymbolName : " globe " , accessibilityDescription : nil )
menu . addItem ( ipv6Item )
let dnsTitle = String ( localized : " DNS Flush: AUTO " )
let dnsItem = NSMenuItem ( title : dnsTitle , action : nil , keyEquivalent : " " )
dnsItem . isEnabled = false
dnsItem . image = NSImage ( systemSymbolName : " arrow.triangle.2.circlepath " , accessibilityDescription : nil )
menu . addItem ( dnsItem )
menu . addItem ( NSMenuItem . separator ( ) )
2026-08-13 10:35:10 -05:00
// 4 . A b o u t P a n e l O p t i o n
2026-08-12 23:44:59 -05:00
let aboutTitle = String ( localized : " About Tethering Hider... " )
let aboutItem = NSMenuItem ( title : aboutTitle , action : #selector ( aboutAction ) , keyEquivalent : " " )
aboutItem . target = self
aboutItem . image = NSImage ( systemSymbolName : " info.circle.fill " , accessibilityDescription : nil )
menu . addItem ( aboutItem )
menu . addItem ( NSMenuItem . separator ( ) )
2026-08-13 10:35:10 -05:00
// 5 . Q u i t O p t i o n w i t h S t a n d a r d m a c O S E x i t I c o n
2026-08-12 23:44:59 -05:00
let quitTitle = String ( localized : " Quit Tethering Hider " )
let quitItem = NSMenuItem ( title : quitTitle , action : #selector ( quitAction ) , keyEquivalent : " q " )
quitItem . target = self
quitItem . image = NSImage ( systemSymbolName : " rectangle.portrait.and.arrow.right " , accessibilityDescription : nil )
menu . addItem ( quitItem )
statusItem . menu = menu
}
func menuWillOpen ( _ menu : NSMenu ) {
ttlManager . refreshStatus ( )
}
@objc private func toggleAction ( ) {
Task { @ MainActor in
do {
let authenticated = try await authService . authenticateWithBiometrics ( )
guard authenticated else { return }
if ttlManager . isProtected {
try await commandService . resetTTL ( )
ttlManager . didSetTTL ( ttlManager . defaultTTL )
} else {
try await commandService . setTTL ( ttlManager . targetTTL )
ttlManager . didSetTTL ( ttlManager . targetTTL )
}
updateMenu ( )
} catch {
ttlManager . setError ( error . localizedDescription )
}
}
}
@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 \n Designed for macOS. " )
2026-08-13 10:35:10 -05:00
var options : [ NSApplication . AboutPanelOptionKey : Any ] = [
2026-08-12 23:44:59 -05:00
. credits : NSAttributedString (
string : creditsString ,
attributes : [
. font : NSFont . systemFont ( ofSize : 11 ) ,
. foregroundColor : NSColor . secondaryLabelColor
]
) ,
. applicationName : " Tethering Hider " ,
. version : " 1.0.0 "
]
2026-08-13 10:35:10 -05:00
if let appIcon = NSImage ( named : NSImage . applicationIconName ) ? ? NSApplication . shared . applicationIconImage {
options [ . applicationIcon ] = appIcon
}
2026-08-12 23:44:59 -05:00
NSApp . orderFrontStandardAboutPanel ( options : options )
NSApp . activate ( ignoringOtherApps : true )
}
@objc private func quitAction ( ) {
NSApp . terminate ( nil )
}
}
@ MainActor
final class AppDelegate : NSObject , NSApplicationDelegate {
var statusBarController : StatusBarController ?
let ttlManager = TTLManager ( )
func applicationWillFinishLaunching ( _ notification : Notification ) {
NSApp . setActivationPolicy ( . accessory )
}
func applicationDidFinishLaunching ( _ notification : Notification ) {
NSApp . setActivationPolicy ( . accessory )
self . statusBarController = StatusBarController ( ttlManager : ttlManager )
}
}
@ main
struct TetheringHiderApp : App {
@ NSApplicationDelegateAdaptor ( AppDelegate . self ) var appDelegate
var body : some Scene {
Settings {
EmptyView ( )
}
}
}