Add source code
This commit is contained in:
parent
69c08de01c
commit
0416d157d7
14 changed files with 1672 additions and 0 deletions
207
TetheringHider/Views/TTLControlPanel.swift
Normal file
207
TetheringHider/Views/TTLControlPanel.swift
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue