Improve UI
This commit is contained in:
@@ -0,0 +1,85 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public func FormView(@ViewBuilder content: () -> some View) -> some View {
|
||||||
|
Form {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
#if os(macOS)
|
||||||
|
.formStyle(.grouped)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormTextItem(_ name: LocalizedStringKey, _ value: String) -> some View {
|
||||||
|
HStack {
|
||||||
|
Text(name)
|
||||||
|
Spacer()
|
||||||
|
Text(value)
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
|
.font(Font.system(.caption, design: .monospaced))
|
||||||
|
#if os(iOS) || os(macOS)
|
||||||
|
.textSelection(.enabled)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormTextItem(_ name: LocalizedStringKey, _ systemImage: String, @ViewBuilder _ value: () -> some View) -> some View {
|
||||||
|
HStack {
|
||||||
|
Label(name, systemImage: systemImage)
|
||||||
|
Spacer()
|
||||||
|
value()
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
|
.font(Font.system(.caption, design: .monospaced))
|
||||||
|
#if os(iOS) || os(macOS)
|
||||||
|
.textSelection(.enabled)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormItem(_ title: String, @ViewBuilder content: () -> some View) -> some View {
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
HStack {
|
||||||
|
Text(title)
|
||||||
|
.lineLimit(1)
|
||||||
|
.layoutPriority(1)
|
||||||
|
Spacer()
|
||||||
|
Spacer()
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
#elseif os(macOS)
|
||||||
|
content()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormSection(@ViewBuilder content: () -> some View, @ViewBuilder footer: () -> some View) -> some View {
|
||||||
|
Section {
|
||||||
|
content()
|
||||||
|
} footer: {
|
||||||
|
footer()
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormButton(action: @escaping () -> Void, @ViewBuilder label: () -> some View) -> some View {
|
||||||
|
Button(action: action, label: label)
|
||||||
|
#if os(macOS)
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormButton(_ titleKey: some StringProtocol, action: @escaping () -> Void) -> some View {
|
||||||
|
Button(titleKey, action: action)
|
||||||
|
#if os(macOS)
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public func FormButton(role: ButtonRole?, action: @escaping () -> Void, @ViewBuilder label: () -> some View) -> some View {
|
||||||
|
Button(role: role, action: action, label: label)
|
||||||
|
#if os(macOS)
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
public func FormView(@ViewBuilder content: () -> some View) -> some View {
|
|
||||||
Form {
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
#if os(macOS)
|
|
||||||
.formStyle(.grouped)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public func FormTextItem(_ name: String, _ value: String) -> some View {
|
|
||||||
HStack {
|
|
||||||
Text(name)
|
|
||||||
Spacer()
|
|
||||||
Text(value)
|
|
||||||
.multilineTextAlignment(.trailing)
|
|
||||||
.font(Font.system(.caption, design: .monospaced))
|
|
||||||
#if os(iOS) || os(macOS)
|
|
||||||
.textSelection(.enabled)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func FormItem(_ title: String, @ViewBuilder content: () -> some View) -> some View {
|
|
||||||
#if os(iOS) || os(tvOS)
|
|
||||||
HStack {
|
|
||||||
Text(title)
|
|
||||||
.lineLimit(1)
|
|
||||||
.layoutPriority(1)
|
|
||||||
Spacer()
|
|
||||||
Spacer()
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
#elseif os(macOS)
|
|
||||||
content()
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#if !os(tvOS)
|
||||||
|
|
||||||
|
import StoreKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public func RequestReviewButton(label: @escaping () -> some View) -> some View {
|
||||||
|
viewBuilder {
|
||||||
|
if #available(iOS 16.0, macOS 13.0, visionOS 1.0, *) {
|
||||||
|
RequestReviewButton0(label: label)
|
||||||
|
} else {
|
||||||
|
#if os(iOS)
|
||||||
|
RequestReviewButton1(label: label)
|
||||||
|
#else
|
||||||
|
EmptyView()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 16.0, macOS 13.0, visionOS 1.0, *)
|
||||||
|
struct RequestReviewButton0<Label: View>: View {
|
||||||
|
@Environment(\.requestReview) private var requestReview
|
||||||
|
|
||||||
|
private let label: () -> Label
|
||||||
|
init(label: @escaping () -> Label) {
|
||||||
|
self.label = label
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
FormButton(action: {
|
||||||
|
requestReview()
|
||||||
|
}, label: label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RequestReviewButton1<Label: View>: View {
|
||||||
|
private let label: () -> Label
|
||||||
|
init(label: @escaping () -> Label) {
|
||||||
|
self.label = label
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button(action: {
|
||||||
|
SKStoreReviewController.requestReview()
|
||||||
|
}, label: label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Library
|
import Library
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
#if os(iOS)
|
#if canImport(UIKit)
|
||||||
import UIKit
|
import UIKit
|
||||||
#elseif os(macOS)
|
#elseif canImport(AppKit)
|
||||||
import AppKit
|
import AppKit
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ public struct InstallProfileButton: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
Button("Install NetworkExtension") {
|
FormButton {
|
||||||
Task {
|
Task {
|
||||||
await installProfile()
|
await installProfile()
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Install Network Extension", systemImage: "lock.doc.fill")
|
||||||
}
|
}
|
||||||
.alertBinding($alert)
|
.alertBinding($alert)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
Button("Install SystemExtension") {
|
FormButton {
|
||||||
Task {
|
Task {
|
||||||
await installSystemExtension()
|
await installSystemExtension()
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Install System Extension", systemImage: "lock.doc.fill")
|
||||||
}
|
}
|
||||||
.alertBinding($alert)
|
.alertBinding($alert)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,15 @@ public enum NavigationPage: Int, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension NavigationPage {
|
public extension NavigationPage {
|
||||||
static var macosDefaultPages: [NavigationPage] {
|
#if os(macOS)
|
||||||
[.logs, .profiles, .settings]
|
static var macosDefaultPages: [NavigationPage] {
|
||||||
}
|
[.logs, .profiles, .settings]
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var label: some View {
|
var label: some View {
|
||||||
Label(title, systemImage: iconImage)
|
Label(title, systemImage: iconImage)
|
||||||
|
.tint(.textColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
var title: String {
|
var title: String {
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct EditProfileContentView: View {
|
public struct EditProfileContentView: View {
|
||||||
#if os(macOS)
|
|
||||||
public static let windowID = "edit-profile-content"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public struct Context: Codable, Hashable {
|
public struct Context: Codable, Hashable {
|
||||||
public let profileID: Int64
|
public let profileID: Int64
|
||||||
public let readOnly: Bool
|
public let readOnly: Bool
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ import SwiftUI
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct EditProfileView: View {
|
public struct EditProfileView: View {
|
||||||
#if os(macOS)
|
|
||||||
@Environment(\.openWindow) private var openWindow
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
@EnvironmentObject private var profile: Profile
|
@EnvironmentObject private var profile: Profile
|
||||||
@@ -58,39 +54,45 @@ public struct EditProfileView: View {
|
|||||||
FormTextItem("Last Updated", profile.lastUpdatedString)
|
FormTextItem("Last Updated", profile.lastUpdatedString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(iOS) || os(tvOS)
|
Section("Action") {
|
||||||
Section("Action") {
|
if profile.type != .remote {
|
||||||
if profile.type != .remote {
|
#if os(iOS) || os(macOS)
|
||||||
#if os(iOS)
|
NavigationLink {
|
||||||
NavigationLink {
|
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
|
||||||
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
|
} label: {
|
||||||
} label: {
|
Label("Edit Content", systemImage: "pencil")
|
||||||
Text("Edit Content").foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
#if os(iOS)
|
|
||||||
NavigationLink {
|
|
||||||
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
|
|
||||||
} label: {
|
|
||||||
Text("View Content").foregroundColor(.accentColor)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Button("Update") {
|
|
||||||
isLoading = true
|
|
||||||
Task {
|
|
||||||
await updateProfile()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.disabled(isLoading)
|
#endif
|
||||||
}
|
} else {
|
||||||
Button("Delete", role: .destructive) {
|
#if os(iOS) || os(macOS)
|
||||||
|
NavigationLink {
|
||||||
|
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
|
||||||
|
} label: {
|
||||||
|
Label("View Content", systemImage: "doc.fill")
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FormButton {
|
||||||
|
isLoading = true
|
||||||
Task {
|
Task {
|
||||||
await deleteProfile()
|
await updateProfile()
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Update", systemImage: "arrow.clockwise")
|
||||||
}
|
}
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.disabled(isLoading)
|
||||||
}
|
}
|
||||||
#endif
|
FormButton(role: .destructive) {
|
||||||
|
Task {
|
||||||
|
await deleteProfile()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Delete", systemImage: "trash.fill")
|
||||||
|
}
|
||||||
|
.foregroundColor(.red)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onChangeCompat(of: profile.name) {
|
.onChangeCompat(of: profile.name) {
|
||||||
isChanged = true
|
isChanged = true
|
||||||
@@ -114,30 +116,6 @@ public struct EditProfileView: View {
|
|||||||
Image("save", bundle: ApplicationLibrary.bundle, label: Text("Save"))
|
Image("save", bundle: ApplicationLibrary.bundle, label: Text("Save"))
|
||||||
}
|
}
|
||||||
.disabled(isLoading || !isChanged)
|
.disabled(isLoading || !isChanged)
|
||||||
if profile.type != .remote {
|
|
||||||
Button {
|
|
||||||
openWindow(id: EditProfileContentView.windowID, value: EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
|
|
||||||
} label: {
|
|
||||||
Label("Edit Content", systemImage: "pencil")
|
|
||||||
}
|
|
||||||
.disabled(isLoading)
|
|
||||||
} else {
|
|
||||||
Button {
|
|
||||||
isLoading = true
|
|
||||||
Task {
|
|
||||||
await updateProfile()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Label("Update", systemImage: "arrow.clockwise")
|
|
||||||
}
|
|
||||||
.disabled(isLoading)
|
|
||||||
Button {
|
|
||||||
openWindow(id: EditProfileContentView.windowID, value: EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
|
|
||||||
} label: {
|
|
||||||
Label("View Content", systemImage: "doc.text.fill")
|
|
||||||
}
|
|
||||||
.disabled(isLoading)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elseif os(iOS)
|
#elseif os(iOS)
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
import Library
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
@MainActor
|
|
||||||
public struct EditProfileWindowView: View {
|
|
||||||
public static let windowID = "edit-profile"
|
|
||||||
|
|
||||||
private var profileID: Int64?
|
|
||||||
|
|
||||||
public init(_ profileID: Int64?) {
|
|
||||||
self.profileID = profileID
|
|
||||||
}
|
|
||||||
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
|
||||||
|
|
||||||
@State private var isLoading = true
|
|
||||||
@State private var profile: Profile!
|
|
||||||
@State private var alert: Alert?
|
|
||||||
|
|
||||||
public var body: some View {
|
|
||||||
viewBuilder {
|
|
||||||
if isLoading {
|
|
||||||
ProgressView().onAppear {
|
|
||||||
Task {
|
|
||||||
await doReload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EditProfileView().environmentObject(profile!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alertBinding($alert)
|
|
||||||
.onExitCommand {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func doReload() async {
|
|
||||||
guard let profileID else {
|
|
||||||
alert = Alert(errorMessage: "Context destroyed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
profile = try await ProfileManager.get(profileID)
|
|
||||||
} catch {
|
|
||||||
alert = Alert(error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if profile == nil {
|
|
||||||
alert = Alert(errorMessage: "Profile deleted")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -5,10 +5,6 @@ import SwiftUI
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct NewProfileView: View {
|
public struct NewProfileView: View {
|
||||||
#if os(macOS)
|
|
||||||
public static let windowID = "new-profile"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
@@ -100,11 +96,13 @@ public struct NewProfileView: View {
|
|||||||
}
|
}
|
||||||
Section {
|
Section {
|
||||||
if !isSaving {
|
if !isSaving {
|
||||||
Button("Create") {
|
FormButton {
|
||||||
isSaving = true
|
isSaving = true
|
||||||
Task {
|
Task {
|
||||||
await createProfile()
|
await createProfile()
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Create", systemImage: "doc.fill.badge.plus")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ public struct ProfileView: View {
|
|||||||
|
|
||||||
#if os(iOS) || os(tvOS)
|
#if os(iOS) || os(tvOS)
|
||||||
@State private var editMode = EditMode.inactive
|
@State private var editMode = EditMode.inactive
|
||||||
#elseif os(macOS)
|
|
||||||
@Environment(\.openWindow) private var openWindow
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
@@ -39,73 +37,67 @@ public struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
#if os(iOS) || os(tvOS)
|
ZStack {
|
||||||
ZStack {
|
if let importRemoteProfileRequest {
|
||||||
if let importRemoteProfileRequest {
|
NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) {
|
||||||
NavigationDestinationCompat(isPresented: $importRemoteProfilePresented) {
|
NewProfileView(importRemoteProfileRequest)
|
||||||
NewProfileView(importRemoteProfileRequest)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FormView {
|
}
|
||||||
#if os(iOS)
|
FormView {
|
||||||
|
#if os(iOS)
|
||||||
|
NavigationLink {
|
||||||
|
NewProfileView()
|
||||||
|
} label: {
|
||||||
|
Text("New Profile").foregroundColor(.accentColor)
|
||||||
|
}
|
||||||
|
.disabled(editMode.isEditing)
|
||||||
|
#elseif os(macOS)
|
||||||
|
NavigationLink {
|
||||||
|
NewProfileView()
|
||||||
|
} label: {
|
||||||
|
Text("New Profile")
|
||||||
|
}
|
||||||
|
#elseif os(tvOS)
|
||||||
|
Section {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
NewProfileView()
|
NewProfileView()
|
||||||
} label: {
|
} label: {
|
||||||
Text("New Profile").foregroundColor(.accentColor)
|
Text("New Profile").foregroundColor(.accentColor)
|
||||||
}
|
}
|
||||||
.disabled(editMode.isEditing)
|
if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) {
|
||||||
#elseif os(tvOS)
|
|
||||||
Section {
|
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
NewProfileView()
|
ImportProfileView {
|
||||||
} label: {
|
await doReload()
|
||||||
Text("New Profile").foregroundColor(.accentColor)
|
|
||||||
}
|
|
||||||
if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) {
|
|
||||||
NavigationLink {
|
|
||||||
ImportProfileView {
|
|
||||||
await doReload()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Text("Import Profile").foregroundColor(.accentColor)
|
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Text("Import Profile").foregroundColor(.accentColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
if profileList.isEmpty {
|
#endif
|
||||||
Text("Empty profiles")
|
if profileList.isEmpty {
|
||||||
} else {
|
Text("Empty profiles")
|
||||||
List {
|
} else {
|
||||||
ForEach(profileList, id: \.id) { profile in
|
List {
|
||||||
viewBuilder {
|
ForEach(profileList, id: \.id) { profile in
|
||||||
|
viewBuilder {
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
if editMode.isEditing == true {
|
if editMode.isEditing == true {
|
||||||
Text(profile.name)
|
Text(profile.name)
|
||||||
} else {
|
} else {
|
||||||
ProfileItem(self, profile)
|
ProfileItem(self, profile)
|
||||||
}
|
}
|
||||||
}
|
#else
|
||||||
|
ProfileItem(self, profile)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
.onMove(perform: moveProfile)
|
|
||||||
.onDelete(perform: deleteProfile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elseif os(macOS)
|
|
||||||
if profileList.isEmpty {
|
|
||||||
Text("Empty profiles")
|
|
||||||
} else {
|
|
||||||
FormView {
|
|
||||||
List {
|
|
||||||
ForEach(profileList, id: \.id) { profile in
|
|
||||||
ProfileItem(self, profile)
|
|
||||||
}
|
}
|
||||||
.onMove(perform: moveProfile)
|
.onMove(perform: moveProfile)
|
||||||
.onDelete(perform: deleteProfile)
|
.onDelete(perform: deleteProfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.disabled(isUpdating)
|
.disabled(isUpdating)
|
||||||
@@ -140,17 +132,7 @@ public struct ProfileView: View {
|
|||||||
// await doReload()
|
// await doReload()
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
#if os(macOS)
|
#if os(iOS)
|
||||||
.toolbar {
|
|
||||||
ToolbarItem {
|
|
||||||
Button {
|
|
||||||
openWindow(id: NewProfileView.windowID)
|
|
||||||
} label: {
|
|
||||||
Label("New Profile", systemImage: "plus.square.fill")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elseif os(iOS)
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
EditButton().disabled(profileList.isEmpty)
|
EditButton().disabled(profileList.isEmpty)
|
||||||
@@ -202,11 +184,7 @@ public struct ProfileView: View {
|
|||||||
title: Text("Import Remote Profile"),
|
title: Text("Import Remote Profile"),
|
||||||
message: Text("Are you sure to import remote profile \(newValue.name)? You will connect to \(newValue.host) to download the configuration."),
|
message: Text("Are you sure to import remote profile \(newValue.name)? You will connect to \(newValue.host) to download the configuration."),
|
||||||
primaryButton: .default(Text("Import")) {
|
primaryButton: .default(Text("Import")) {
|
||||||
#if os(iOS) || os(tvOS)
|
importRemoteProfilePresented = true
|
||||||
importRemoteProfilePresented = true
|
|
||||||
#elseif os(macOS)
|
|
||||||
openWindow(id: NewProfileView.windowID, value: importRemoteProfileRequest!)
|
|
||||||
#endif
|
|
||||||
},
|
},
|
||||||
secondaryButton: .cancel()
|
secondaryButton: .cancel()
|
||||||
)
|
)
|
||||||
@@ -285,6 +263,7 @@ public struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
public struct ProfileItem: View {
|
public struct ProfileItem: View {
|
||||||
private let parent: ProfileView
|
private let parent: ProfileView
|
||||||
@State private var profile: ProfilePreview
|
@State private var profile: ProfilePreview
|
||||||
@@ -307,7 +286,6 @@ public struct ProfileView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
|
||||||
private var body0: some View {
|
private var body0: some View {
|
||||||
viewBuilder {
|
viewBuilder {
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
@@ -346,57 +324,63 @@ public struct ProfileView: View {
|
|||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("Delete", systemImage: "trash.fill")
|
Label("Delete", systemImage: "trash.fill")
|
||||||
|
.foregroundColor(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
HStack {
|
NavigationLink {
|
||||||
VStack(alignment: .leading) {
|
EditProfileView().environmentObject(profile.origin)
|
||||||
Text(profile.name)
|
} label: {
|
||||||
if profile.type == .remote {
|
|
||||||
Spacer(minLength: 4)
|
|
||||||
Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HStack {
|
HStack {
|
||||||
if profile.type == .remote {
|
VStack(alignment: .leading) {
|
||||||
|
Text(profile.name)
|
||||||
|
if profile.type == .remote {
|
||||||
|
Spacer(minLength: 4)
|
||||||
|
Text("Last Updated: \(profile.origin.lastUpdatedString)").font(.caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
if profile.type == .remote {
|
||||||
|
Button {
|
||||||
|
parent.isUpdating = true
|
||||||
|
Task {
|
||||||
|
await parent.updateProfile(profile.origin)
|
||||||
|
profile = ProfilePreview(profile.origin)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "arrow.clockwise")
|
||||||
|
}
|
||||||
|
.padding(.leading, 4)
|
||||||
|
|
||||||
|
Button {
|
||||||
|
shareLinkPresented = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "qrcode")
|
||||||
|
}
|
||||||
|
.padding(.leading, 4)
|
||||||
|
.popover(isPresented: $shareLinkPresented, arrowEdge: .bottom) {
|
||||||
|
shareLinkView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProfileShareButton(parent.$alert, profile.origin) {
|
||||||
|
Image(systemName: "square.and.arrow.up.fill")
|
||||||
|
}
|
||||||
|
.padding(.leading, 4)
|
||||||
Button {
|
Button {
|
||||||
parent.isUpdating = true
|
|
||||||
Task {
|
Task {
|
||||||
await parent.updateProfile(profile.origin)
|
await parent.deleteProfile(profile.origin)
|
||||||
profile = ProfilePreview(profile.origin)
|
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Image(systemName: "arrow.clockwise")
|
Image(systemName: "trash.fill")
|
||||||
}
|
|
||||||
Button {
|
|
||||||
shareLinkPresented = true
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "qrcode")
|
|
||||||
}
|
|
||||||
.popover(isPresented: $shareLinkPresented, arrowEdge: .bottom) {
|
|
||||||
shareLinkView
|
|
||||||
}
|
}
|
||||||
|
.padding([.leading, .trailing], 4)
|
||||||
}
|
}
|
||||||
ProfileShareButton(parent.$alert, profile.origin) {
|
.buttonStyle(.plain)
|
||||||
Image(systemName: "square.and.arrow.up.fill")
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
}
|
|
||||||
Button {
|
|
||||||
parent.openWindow(id: EditProfileWindowView.windowID, value: profile.id)
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "pencil")
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Task {
|
|
||||||
await parent.deleteProfile(profile.origin)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "trash.fill")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
.padding(.vertical, 8)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,6 +396,9 @@ public struct ProfileView: View {
|
|||||||
shareLinkView0
|
shareLinkView0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#elseif os(macOS)
|
||||||
|
shareLinkView0
|
||||||
|
.frame(minWidth: 300, minHeight: 300)
|
||||||
#else
|
#else
|
||||||
shareLinkView0
|
shareLinkView0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
import Libbox
|
||||||
|
import Library
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct CoreView: View {
|
||||||
|
@State private var isLoading = true
|
||||||
|
|
||||||
|
@State private var version = ""
|
||||||
|
@State private var dataSize = ""
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
public var body: some View {
|
||||||
|
viewBuilder {
|
||||||
|
if isLoading {
|
||||||
|
ProgressView().onAppear {
|
||||||
|
Task {
|
||||||
|
await loadSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FormView {
|
||||||
|
FormTextItem("Version", version)
|
||||||
|
FormTextItem("Data Size", dataSize)
|
||||||
|
|
||||||
|
Section("Working Directory") {
|
||||||
|
#if os(macOS)
|
||||||
|
FormButton {
|
||||||
|
NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: FilePath.workingDirectory.relativePath)
|
||||||
|
} label: {
|
||||||
|
Label("Open", systemImage: "macwindow.and.cursorarrow")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FormButton {
|
||||||
|
Task {
|
||||||
|
await destroyWorkingDirectory()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Destroy", systemImage: "trash.fill")
|
||||||
|
}
|
||||||
|
.foregroundColor(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Core")
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private nonisolated func loadSettings() async {
|
||||||
|
if ApplicationLibrary.inPreview {
|
||||||
|
version = "<redacted>"
|
||||||
|
dataSize = LibboxFormatBytes(1000 * 1000 * 10)
|
||||||
|
isLoading = false
|
||||||
|
} else {
|
||||||
|
version = LibboxVersion()
|
||||||
|
dataSize = "Loading..."
|
||||||
|
isLoading = false
|
||||||
|
await loadSettingsBackground()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private nonisolated func loadSettingsBackground() async {
|
||||||
|
let dataSize = (try? FilePath.workingDirectory.formattedSize()) ?? "Unknown"
|
||||||
|
await MainActor.run {
|
||||||
|
self.dataSize = dataSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private nonisolated func destroyWorkingDirectory() async {
|
||||||
|
try? FileManager.default.removeItem(at: FilePath.workingDirectory)
|
||||||
|
await MainActor.run {
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension URL {
|
||||||
|
func formattedSize() throws -> String? {
|
||||||
|
guard let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as? [URL] else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let size = try urls.lazy.reduce(0) {
|
||||||
|
try ($1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0
|
||||||
|
}
|
||||||
|
let formatter = ByteCountFormatter()
|
||||||
|
formatter.countStyle = .file
|
||||||
|
guard let byteCount = formatter.string(for: size) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return byteCount
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#if os(macOS)
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import Library
|
||||||
|
import ServiceManagement
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct MacAppView: View {
|
||||||
|
@State private var isLoading = true
|
||||||
|
|
||||||
|
@State private var startAtLogin = false
|
||||||
|
@Environment(\.showMenuBarExtra) private var showMenuBarExtra
|
||||||
|
@State private var menuBarExtraInBackground = false
|
||||||
|
|
||||||
|
@State private var alert: Alert?
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
public var body: some View {
|
||||||
|
viewBuilder {
|
||||||
|
if isLoading {
|
||||||
|
ProgressView().onAppear {
|
||||||
|
Task {
|
||||||
|
await loadSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FormView {
|
||||||
|
FormSection {
|
||||||
|
Toggle("Start At Login", isOn: $startAtLogin)
|
||||||
|
.onChangeCompat(of: startAtLogin) { newValue in
|
||||||
|
Task {
|
||||||
|
updateLoginItems(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("Launch the application when the system is logged in. If enabled at the same time as `Show in Menu Bar` and `Keep Menu Bar in Background`, the application interface will not be opened automatically.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Toggle("Show in Menu Bar", isOn: showMenuBarExtra)
|
||||||
|
.onChangeCompat(of: showMenuBarExtra.wrappedValue) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.showMenuBarExtra.set(newValue)
|
||||||
|
if !newValue {
|
||||||
|
menuBarExtraInBackground = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if showMenuBarExtra.wrappedValue {
|
||||||
|
Toggle("Keep Menu Bar in Background", isOn: $menuBarExtraInBackground)
|
||||||
|
.onChangeCompat(of: menuBarExtraInBackground) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.menuBarExtraInBackground.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if Variant.useSystemExtension {
|
||||||
|
Section("System Extension") {
|
||||||
|
FormButton {
|
||||||
|
Task {
|
||||||
|
await updateSystemExtension()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Update", systemImage: "arrow.down.doc.fill")
|
||||||
|
}
|
||||||
|
FormButton {
|
||||||
|
Task {
|
||||||
|
await uninstallSystemExtension()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Uninstall", systemImage: "trash.fill").foregroundColor(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alertBinding($alert)
|
||||||
|
.navigationTitle("App")
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadSettings() async {
|
||||||
|
startAtLogin = SMAppService.mainApp.status == .enabled
|
||||||
|
menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get()
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLoginItems(_ startAtLogin: Bool) {
|
||||||
|
do {
|
||||||
|
if startAtLogin {
|
||||||
|
if SMAppService.mainApp.status == .enabled {
|
||||||
|
try? SMAppService.mainApp.unregister()
|
||||||
|
}
|
||||||
|
|
||||||
|
try SMAppService.mainApp.register()
|
||||||
|
} else {
|
||||||
|
try SMAppService.mainApp.unregister()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
alert = Alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateSystemExtension() async {
|
||||||
|
do {
|
||||||
|
if let result = try await SystemExtension.install(forceUpdate: true) {
|
||||||
|
switch result {
|
||||||
|
case .completed:
|
||||||
|
alert = Alert(
|
||||||
|
title: Text("Update"),
|
||||||
|
message: Text("System Extension updated."),
|
||||||
|
dismissButton: .default(Text("Ok")) {}
|
||||||
|
)
|
||||||
|
case .willCompleteAfterReboot:
|
||||||
|
alert = Alert(
|
||||||
|
title: Text("Update"),
|
||||||
|
message: Text("Reboot required."),
|
||||||
|
dismissButton: .default(Text("Ok")) {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
alert = Alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func uninstallSystemExtension() async {
|
||||||
|
do {
|
||||||
|
if let result = try await SystemExtension.uninstall() {
|
||||||
|
switch result {
|
||||||
|
case .completed:
|
||||||
|
alert = Alert(
|
||||||
|
title: Text("Uninstall"),
|
||||||
|
message: Text("System Extension removed."),
|
||||||
|
dismissButton: .default(Text("Ok")) {}
|
||||||
|
)
|
||||||
|
case .willCompleteAfterReboot:
|
||||||
|
alert = Alert(
|
||||||
|
title: Text("Uninstall"),
|
||||||
|
message: Text("Reboot required."),
|
||||||
|
dismissButton: .default(Text("Ok")) {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
alert = Alert(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import Library
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PacketTunnelView: View {
|
||||||
|
#if os(macOS)
|
||||||
|
public static let windowID = "packet-tunnel"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@State private var isLoading = true
|
||||||
|
|
||||||
|
@State private var ignoreMemoryLimit = false
|
||||||
|
@State private var ignoreDeviceSleep = false
|
||||||
|
|
||||||
|
@State private var includeAllNetworks = false
|
||||||
|
@State private var excludeAPNs = false
|
||||||
|
@State private var excludeCellularServices = false
|
||||||
|
@State private var excludeLocalNetworks = false
|
||||||
|
@State private var enforceRoutes = false
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
public var body: some View {
|
||||||
|
viewBuilder {
|
||||||
|
if isLoading {
|
||||||
|
ProgressView().onAppear {
|
||||||
|
Task.detached {
|
||||||
|
await loadSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FormView {
|
||||||
|
FormSection {
|
||||||
|
Toggle("Ignore Memory Limit", isOn: $ignoreMemoryLimit)
|
||||||
|
.onChangeCompat(of: ignoreMemoryLimit) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.ignoreMemoryLimit.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("Do not enforce memory limits on sing-box. Will cause OOM on non-jailbroken iOS and tvOS devices.")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("Ignore Device Sleep", isOn: $ignoreDeviceSleep)
|
||||||
|
.onChangeCompat(of: ignoreDeviceSleep) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.ignoreDeviceSleep.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("Ignore system `sleep()` and `wake()` events. May cause increased power usage, only enable if you encounter unexpected `rejected ... while device paused` errors.")
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !os(tvOS)
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("includeAllNetworks", isOn: $includeAllNetworks)
|
||||||
|
.onChangeCompat(of: includeAllNetworks) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.includeAllNetworks.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("""
|
||||||
|
If this property is true, the system routes network traffic through the tunnel except traffic for designated system services necessary for maintaining expected device functionality. You can exclude some types of traffic using the **excludeAPNs**, **excludeLocalNetworks**, and **excludeCellularServices** properties in combination with this property.
|
||||||
|
|
||||||
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3131931-includeallnetworks)
|
||||||
|
""")
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
}
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("excludeAPNs", isOn: $excludeAPNs)
|
||||||
|
.onChangeCompat(of: excludeAPNs) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.excludeAPNs.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("""
|
||||||
|
If this property is true, the system excludes Apple Push Notification services (APNs) traffic, but only when the **includeAllNetworks** property is also true.
|
||||||
|
|
||||||
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("excludeCellularServices", isOn: $excludeCellularServices)
|
||||||
|
.onChangeCompat(of: excludeCellularServices) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.excludeCellularServices.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("""
|
||||||
|
If this property is true, the system excludes cellular services — such as Wi-Fi Calling, MMS, SMS, and Visual Voicemail — but only when the **includeAllNetworks** property is also true. This property doesn’t impact services that use the cellular network only — such as VoLTE — which the system automatically excludes.
|
||||||
|
|
||||||
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("excludeLocalNetworks", isOn: $excludeLocalNetworks)
|
||||||
|
.onChangeCompat(of: excludeLocalNetworks) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.excludeLocalNetworks.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("""
|
||||||
|
If this property is true, the system excludes network connections to hosts on the local network — such as AirPlay, AirDrop, and CarPlay — but only when the **includeAllNetworks** or **enforceRoutes** property is also true.
|
||||||
|
|
||||||
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("enforceRoutes", isOn: $enforceRoutes)
|
||||||
|
.onChangeCompat(of: enforceRoutes) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.enforceRoutes.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("""
|
||||||
|
If this property is true when the **includeAllNetworks** property is false, the system scopes the included routes to the VPN and the excluded routes to the current primary network interface. This property supersedes the system routing table and scoping operations by apps.
|
||||||
|
|
||||||
|
If you set both the **enforceRoutes** and **excludeLocalNetworks** properties to true, the system excludes network connections to hosts on the local network.
|
||||||
|
|
||||||
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3689459-enforceroutes)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FormButton {
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.resetPacketTunnel()
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Reset", systemImage: "eraser.fill")
|
||||||
|
}
|
||||||
|
.foregroundColor(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Packet Tunnel")
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadSettings() async {
|
||||||
|
ignoreMemoryLimit = await SharedPreferences.ignoreMemoryLimit.get()
|
||||||
|
ignoreDeviceSleep = await SharedPreferences.ignoreDeviceSleep.get()
|
||||||
|
#if !os(tvOS)
|
||||||
|
includeAllNetworks = await SharedPreferences.includeAllNetworks.get()
|
||||||
|
excludeAPNs = await SharedPreferences.excludeAPNs.get()
|
||||||
|
excludeCellularServices = await SharedPreferences.excludeCellularServices.get()
|
||||||
|
excludeLocalNetworks = await SharedPreferences.excludeLocalNetworks.get()
|
||||||
|
enforceRoutes = await SharedPreferences.enforceRoutes.get()
|
||||||
|
#endif
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import Library
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct ProfileOverrideView: View {
|
||||||
|
@State private var isLoading = true
|
||||||
|
@State private var excludeDefaultRoute = false
|
||||||
|
@State private var autoRouteUseSubRangesByDefault = false
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
public var body: some View {
|
||||||
|
viewBuilder {
|
||||||
|
if isLoading {
|
||||||
|
ProgressView().onAppear {
|
||||||
|
Task.detached {
|
||||||
|
await loadSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FormView {
|
||||||
|
FormSection {
|
||||||
|
Toggle("Hide VPN Icon", isOn: $excludeDefaultRoute)
|
||||||
|
.onChangeCompat(of: excludeDefaultRoute) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.excludeDefaultRoute.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("Append `0.0.0.0/31` to `inet4_route_exclude_address` if not exists.")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormSection {
|
||||||
|
Toggle("No Default Route", isOn: $autoRouteUseSubRangesByDefault)
|
||||||
|
.onChangeCompat(of: autoRouteUseSubRangesByDefault) { newValue in
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.autoRouteUseSubRangesByDefault.set(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("By default, segment routing is used in `auto_route` instead of global routing. If `*_<route_address/route_exclude_address>` exists in the configuration, this item will not take effect on the corresponding network. (commonly used to resolve HomeKit compatibility issues)")
|
||||||
|
}
|
||||||
|
|
||||||
|
FormButton {
|
||||||
|
Task {
|
||||||
|
await SharedPreferences.resetProfileOverride()
|
||||||
|
isLoading = true
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Reset", systemImage: "eraser.fill")
|
||||||
|
}
|
||||||
|
.foregroundColor(.red)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Profile Override")
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadSettings() async {
|
||||||
|
excludeDefaultRoute = await SharedPreferences.excludeDefaultRoute.get()
|
||||||
|
autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get()
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Library
|
import Library
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UniformTypeIdentifiers
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public struct ServiceLogView: View {
|
public struct ServiceLogView: View {
|
||||||
#if os(macOS)
|
|
||||||
public static let windowID = "service-log"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
|
||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
@State private var content = ""
|
@State private var content = ""
|
||||||
@State private var fileExporterPresented = false
|
@State private var alert: Alert?
|
||||||
|
|
||||||
private let logFont = Font.system(.caption, design: .monospaced)
|
private let logFont = Font.system(.caption, design: .monospaced)
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
@@ -41,26 +38,22 @@ public struct ServiceLogView: View {
|
|||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
if !content.isEmpty {
|
if !content.isEmpty {
|
||||||
Button("Export") {
|
ShareButtonCompat($alert) {
|
||||||
fileExporterPresented = true
|
Label("Export", systemImage: "square.and.arrow.up.fill")
|
||||||
|
} itemURL: {
|
||||||
|
try content.generateShareFile(name: "service.log")
|
||||||
}
|
}
|
||||||
Button("Delete", role: .destructive) {
|
Button(role: .destructive) {
|
||||||
Task {
|
Task {
|
||||||
await deleteContent()
|
await deleteContent()
|
||||||
}
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Delete", systemImage: "trash.fill")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if !os(tvOS)
|
.alertBinding($alert)
|
||||||
.fileExporter(
|
|
||||||
isPresented: $fileExporterPresented,
|
|
||||||
document: LogDocument(content),
|
|
||||||
contentType: .text,
|
|
||||||
defaultFilename: "service-log.txt",
|
|
||||||
onCompletion: { _ in }
|
|
||||||
)
|
|
||||||
#endif
|
|
||||||
.navigationTitle("Service Log")
|
.navigationTitle("Service Log")
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
.focusable()
|
.focusable()
|
||||||
@@ -77,6 +70,27 @@ public struct ServiceLogView: View {
|
|||||||
content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old"))
|
content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old"))
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
|
if content.isEmpty {
|
||||||
|
content = "Empty content"
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if !content.isEmpty {
|
||||||
|
var systemInfo = utsname()
|
||||||
|
uname(&systemInfo)
|
||||||
|
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||||
|
let machineName = machineMirror.children.reduce("") { identifier, element in
|
||||||
|
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
||||||
|
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||||
|
}
|
||||||
|
var deviceInfo = await "Machine: " + machineName + "\n"
|
||||||
|
#if os(iOS)
|
||||||
|
await deviceInfo += "System: " + (UIDevice.current.systemName) + " " + (UIDevice.current.systemVersion) + "\n"
|
||||||
|
#elseif os(macOS)
|
||||||
|
deviceInfo += "System: macOS " + ProcessInfo().operatingSystemVersionString + "\n"
|
||||||
|
#endif
|
||||||
|
content = deviceInfo + "\n" + content
|
||||||
|
}
|
||||||
await MainActor.run { [content] in
|
await MainActor.run { [content] in
|
||||||
self.content = content
|
self.content = content
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@@ -91,28 +105,4 @@ public struct ServiceLogView: View {
|
|||||||
isLoading = true
|
isLoading = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
|
||||||
private struct LogDocument: FileDocument {
|
|
||||||
static var readableContentTypes = [UTType.text]
|
|
||||||
|
|
||||||
let content: String
|
|
||||||
|
|
||||||
init(_ content: String) {
|
|
||||||
self.content = content
|
|
||||||
}
|
|
||||||
|
|
||||||
init(configuration: ReadConfiguration) throws {
|
|
||||||
if let data = configuration.file.regularFileContents {
|
|
||||||
content = String(decoding: data, as: UTF8.self)
|
|
||||||
} else {
|
|
||||||
content = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileWrapper(configuration _: WriteConfiguration) throws -> FileWrapper {
|
|
||||||
FileWrapper(regularFileWithContents: Data(content.utf8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,286 +1,137 @@
|
|||||||
import Foundation
|
|
||||||
import Libbox
|
import StoreKit
|
||||||
import Library
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
#if os(macOS)
|
|
||||||
import AppKit
|
|
||||||
import ServiceManagement
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
public struct SettingView: View {
|
public struct SettingView: View {
|
||||||
#if os(macOS)
|
private enum Tabs: Int, CaseIterable, Identifiable {
|
||||||
@Environment(\.openWindow) private var openWindow
|
public var id: Self {
|
||||||
#endif
|
self
|
||||||
|
}
|
||||||
|
|
||||||
@State private var isLoading = true
|
#if os(macOS)
|
||||||
|
case app
|
||||||
|
#endif
|
||||||
|
|
||||||
#if os(macOS)
|
case core, packetTunnel, profileOverride, sponsor
|
||||||
@State private var startAtLogin = false
|
|
||||||
@Environment(\.showMenuBarExtra) private var showMenuBarExtra
|
|
||||||
@State private var keepMenuBarInBackground = false
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@State private var alwaysOn = false
|
var label: some View {
|
||||||
@State private var disableMemoryLimit = false
|
Label(title, systemImage: iconImage)
|
||||||
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
var title: String {
|
||||||
@State private var includeAllNetworks = false
|
switch self {
|
||||||
#endif
|
#if os(macOS)
|
||||||
|
case .app:
|
||||||
|
return NSLocalizedString("App", comment: "")
|
||||||
|
#endif
|
||||||
|
case .core:
|
||||||
|
return NSLocalizedString("Core", comment: "")
|
||||||
|
case .packetTunnel:
|
||||||
|
return NSLocalizedString("Packet Tunnel", comment: "")
|
||||||
|
case .profileOverride:
|
||||||
|
return NSLocalizedString("Profile Override", comment: "")
|
||||||
|
case .sponsor:
|
||||||
|
return NSLocalizedString("Sponsor", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@State private var ignoreDeviceSleep = false
|
private var iconImage: String {
|
||||||
|
switch self {
|
||||||
|
#if os(macOS)
|
||||||
|
case .app:
|
||||||
|
return "app.badge.fill"
|
||||||
|
#endif
|
||||||
|
case .core:
|
||||||
|
return "shippingbox.fill"
|
||||||
|
case .packetTunnel:
|
||||||
|
return "aspectratio.fill"
|
||||||
|
case .profileOverride:
|
||||||
|
return "square.dashed.inset.filled"
|
||||||
|
case .sponsor:
|
||||||
|
return "heart.fill"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@State private var version = ""
|
@MainActor
|
||||||
@State private var dataSize = ""
|
var contentView: some View {
|
||||||
|
viewBuilder {
|
||||||
|
switch self {
|
||||||
|
#if os(macOS)
|
||||||
|
case .app:
|
||||||
|
MacAppView()
|
||||||
|
#endif
|
||||||
|
case .core:
|
||||||
|
CoreView()
|
||||||
|
case .packetTunnel:
|
||||||
|
PacketTunnelView()
|
||||||
|
case .profileOverride:
|
||||||
|
ProfileOverrideView()
|
||||||
|
case .sponsor:
|
||||||
|
SponsorView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||||
|
#if os(iOS)
|
||||||
|
.background(Color(uiColor: .systemGroupedBackground))
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
var navigationLink: some View {
|
||||||
|
NavigationLink {
|
||||||
|
contentView
|
||||||
|
} label: {
|
||||||
|
label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@State private var isLoading = false
|
||||||
@State private var taiwanFlagAvailable = false
|
@State private var taiwanFlagAvailable = false
|
||||||
@State private var alert: Alert?
|
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
viewBuilder {
|
FormView {
|
||||||
if isLoading {
|
#if os(macOS)
|
||||||
ProgressView().onAppear {
|
Tabs.app.navigationLink
|
||||||
Task {
|
#endif
|
||||||
await loadSettings()
|
ForEach([Tabs.core, Tabs.packetTunnel, Tabs.profileOverride]) { it in
|
||||||
}
|
it.navigationLink
|
||||||
|
}
|
||||||
|
Section("About") {
|
||||||
|
Link(destination: URL(string: "https://sing-box.sagernet.org/")!) {
|
||||||
|
Label("Documentation", systemImage: "doc.on.doc.fill")
|
||||||
}
|
}
|
||||||
} else {
|
.buttonStyle(.plain)
|
||||||
FormView {
|
.foregroundColor(.accentColor)
|
||||||
#if os(macOS)
|
#if !os(tvOS)
|
||||||
Section("MacOS") {
|
RequestReviewButton {
|
||||||
Toggle("Start At Login", isOn: $startAtLogin)
|
Label("Rate on the App Store", systemImage: "text.bubble.fill")
|
||||||
.onChangeCompat(of: startAtLogin) { newValue in
|
|
||||||
Task {
|
|
||||||
updateLoginItems(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Toggle("Show in Menu Bar", isOn: showMenuBarExtra)
|
|
||||||
.onChange(of: showMenuBarExtra.wrappedValue) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.showMenuBarExtra.set(newValue)
|
|
||||||
if !newValue {
|
|
||||||
keepMenuBarInBackground = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if showMenuBarExtra.wrappedValue {
|
|
||||||
Toggle("Keep Menu Bar in Background", isOn: $keepMenuBarInBackground)
|
|
||||||
.onChangeCompat(of: keepMenuBarInBackground) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.menuBarExtraInBackground.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Section("Packet Tunnel") {
|
|
||||||
Toggle("Always On", isOn: $alwaysOn)
|
|
||||||
.onChangeCompat(of: alwaysOn) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.alwaysOn.set(newValue)
|
|
||||||
await updateAlwaysOn(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Toggle("Disable Memory Limit", isOn: $disableMemoryLimit)
|
|
||||||
.onChangeCompat(of: disableMemoryLimit) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.disableMemoryLimit.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if !os(tvOS)
|
|
||||||
Toggle("Include All Networks", isOn: $includeAllNetworks)
|
|
||||||
.onChangeCompat(of: includeAllNetworks) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.includeAllNetworks.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Toggle("Ignore Device Sleep", isOn: $ignoreDeviceSleep)
|
|
||||||
.onChangeCompat(of: ignoreDeviceSleep) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.ignoreDeviceSleep.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if os(macOS)
|
|
||||||
if Variant.useSystemExtension {
|
|
||||||
HStack {
|
|
||||||
Button("Update System Extension") {
|
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
if let result = try await SystemExtension.install(forceUpdate: true) {
|
|
||||||
switch result {
|
|
||||||
case .completed:
|
|
||||||
alert = Alert(
|
|
||||||
title: Text("Update"),
|
|
||||||
message: Text("System Extension updated."),
|
|
||||||
dismissButton: .default(Text("Ok")) {}
|
|
||||||
)
|
|
||||||
case .willCompleteAfterReboot:
|
|
||||||
alert = Alert(
|
|
||||||
title: Text("Update"),
|
|
||||||
message: Text("Reboot required."),
|
|
||||||
dismissButton: .default(Text("Ok")) {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert = Alert(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
if let result = try await SystemExtension.uninstall() {
|
|
||||||
switch result {
|
|
||||||
case .completed:
|
|
||||||
alert = Alert(
|
|
||||||
title: Text("Uninstall"),
|
|
||||||
message: Text("System Extension removed."),
|
|
||||||
dismissButton: .default(Text("Ok")) {}
|
|
||||||
)
|
|
||||||
case .willCompleteAfterReboot:
|
|
||||||
alert = Alert(
|
|
||||||
title: Text("Uninstall"),
|
|
||||||
message: Text("Reboot required."),
|
|
||||||
dismissButton: .default(Text("Ok")) {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert = Alert(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Text("Uninstall System Extension").foregroundColor(.red)
|
|
||||||
}
|
|
||||||
}.frame(maxWidth: .infinity, alignment: .trailing)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
Section("Core") {
|
#endif
|
||||||
FormTextItem("Version", version)
|
Tabs.sponsor.navigationLink
|
||||||
FormTextItem("Data Size", dataSize)
|
}
|
||||||
#if os(iOS) || os(tvOS)
|
Section("Debug") {
|
||||||
NavigationLink(destination: ServiceLogView()) {
|
NavigationLink {
|
||||||
Text("View Service Log")
|
ServiceLogView()
|
||||||
}
|
} label: {
|
||||||
Button("Clear Working Directory") {
|
Label("Service Log", systemImage: "doc.on.clipboard")
|
||||||
Task {
|
}
|
||||||
await clearWorkingDirectory()
|
FormTextItem("Taiwan Flag Available", "touchid") {
|
||||||
|
if isLoading {
|
||||||
|
Text("Loading...")
|
||||||
|
.onAppear {
|
||||||
|
Task.detached {
|
||||||
|
taiwanFlagAvailable = !DeviceCensorship.isChinaDevice()
|
||||||
|
isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.foregroundColor(.red)
|
} else {
|
||||||
#elseif os(macOS)
|
Text(taiwanFlagAvailable.description)
|
||||||
HStack {
|
|
||||||
Button("View Service Log") {
|
|
||||||
openWindow(id: ServiceLogView.windowID)
|
|
||||||
}
|
|
||||||
Button("Open Working Directory") {
|
|
||||||
NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: FilePath.workingDirectory.relativePath)
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
Task {
|
|
||||||
await clearWorkingDirectory()
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Text("Clear Working Directory").foregroundColor(.red)
|
|
||||||
}
|
|
||||||
}.frame(maxWidth: .infinity, alignment: .trailing)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
Section("Debug") {
|
|
||||||
FormTextItem("Taiwan Flag Available", taiwanFlagAvailable.description)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alertBinding($alert)
|
.navigationTitle("Settings")
|
||||||
}
|
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
private func updateLoginItems(_ startAtLogin: Bool) {
|
|
||||||
do {
|
|
||||||
if startAtLogin {
|
|
||||||
if SMAppService.mainApp.status == .enabled {
|
|
||||||
try? SMAppService.mainApp.unregister()
|
|
||||||
}
|
|
||||||
|
|
||||||
try SMAppService.mainApp.register()
|
|
||||||
} else {
|
|
||||||
try SMAppService.mainApp.unregister()
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert = Alert(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private func loadSettings() async {
|
|
||||||
#if os(macOS)
|
|
||||||
startAtLogin = SMAppService.mainApp.status == .enabled
|
|
||||||
keepMenuBarInBackground = await SharedPreferences.menuBarExtraInBackground.get()
|
|
||||||
#endif
|
|
||||||
alwaysOn = await SharedPreferences.alwaysOn.get()
|
|
||||||
disableMemoryLimit = await SharedPreferences.disableMemoryLimit.get()
|
|
||||||
#if !os(tvOS)
|
|
||||||
includeAllNetworks = await SharedPreferences.includeAllNetworks.get()
|
|
||||||
#endif
|
|
||||||
ignoreDeviceSleep = await SharedPreferences.ignoreDeviceSleep.get()
|
|
||||||
if ApplicationLibrary.inPreview {
|
|
||||||
version = "<redacted>"
|
|
||||||
dataSize = LibboxFormatBytes(1000 * 1000 * 10)
|
|
||||||
taiwanFlagAvailable = true
|
|
||||||
isLoading = false
|
|
||||||
} else {
|
|
||||||
version = LibboxVersion()
|
|
||||||
dataSize = "Loading..."
|
|
||||||
taiwanFlagAvailable = !DeviceCensorship.isChinaDevice()
|
|
||||||
isLoading = false
|
|
||||||
await loadSettingsBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private nonisolated func loadSettingsBackground() async {
|
|
||||||
let dataSize = (try? FilePath.workingDirectory.formattedSize()) ?? "Unknown"
|
|
||||||
await MainActor.run {
|
|
||||||
self.dataSize = dataSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private nonisolated func clearWorkingDirectory() async {
|
|
||||||
try? FileManager.default.removeItem(at: FilePath.workingDirectory)
|
|
||||||
await MainActor.run {
|
|
||||||
isLoading = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateAlwaysOn(_ newState: Bool) async {
|
|
||||||
guard let profile = try? await ExtensionProfile.load() else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try await profile.updateAlwaysOn(newState)
|
|
||||||
} catch {
|
|
||||||
alert = Alert(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension URL {
|
|
||||||
func formattedSize() throws -> String? {
|
|
||||||
guard let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as? [URL] else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
let size = try urls.lazy.reduce(0) {
|
|
||||||
try ($1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0
|
|
||||||
}
|
|
||||||
let formatter = ByteCountFormatter()
|
|
||||||
formatter.countStyle = .file
|
|
||||||
guard let byteCount = formatter.string(for: size) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return byteCount
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import Foundation
|
||||||
|
import StoreKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct SponsorView: View {
|
||||||
|
@Environment(\.openURL) private var openURL
|
||||||
|
|
||||||
|
@State private var isLoading = true
|
||||||
|
@State private var products: [Product] = []
|
||||||
|
@State private var subscriptionError: Error?
|
||||||
|
@State private var isPurchasing = false
|
||||||
|
@State private var alert: Alert?
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
public var body: some View {
|
||||||
|
FormView {
|
||||||
|
Section {
|
||||||
|
EmptyView()
|
||||||
|
} footer: {
|
||||||
|
Text("**If I’ve defended your modern life, please consider sponsoring me.**")
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
|
||||||
|
Section("Without commission") {
|
||||||
|
FormButton("GitHub Sponsor (recommended)") {
|
||||||
|
openURL(URL(string: "https://github.com/sponsors/nekohasekai")!)
|
||||||
|
}
|
||||||
|
FormButton("Other methods") {
|
||||||
|
openURL(URL(string: "https://sekai.icu/sponsor/")!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Section("Via App Store") {
|
||||||
|
if isLoading {
|
||||||
|
ProgressView()
|
||||||
|
.onAppear {
|
||||||
|
Task.detached {
|
||||||
|
await loadProducts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let subscriptionError {
|
||||||
|
Text("Sponsor via App Store not available: \(subscriptionError.localizedDescription)")
|
||||||
|
} else {
|
||||||
|
ForEach(products, id: \.id) { it in
|
||||||
|
FormButton(it.displayName) {
|
||||||
|
isPurchasing = true
|
||||||
|
Task.detached {
|
||||||
|
do {
|
||||||
|
let result = try await it.purchase()
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
alert = Alert(title: Text("Success"), message: Text("Thank u."))
|
||||||
|
case .pending:
|
||||||
|
break
|
||||||
|
case .userCancelled:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
alert = Alert(error)
|
||||||
|
}
|
||||||
|
isPurchasing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.disabled(isPurchasing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alertBinding($alert)
|
||||||
|
.navigationTitle("Sponsor")
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadProducts() async {
|
||||||
|
defer {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
let productIds = ["sponsor_1_1", "sponsor_10", "sponsor_100"]
|
||||||
|
products = try await Product.products(for: productIds)
|
||||||
|
} catch {
|
||||||
|
subscriptionError = error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,6 +64,16 @@ public extension LibboxProfileContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension String {
|
||||||
|
func generateShareFile(name: String) throws -> URL {
|
||||||
|
let shareDirectory = FilePath.cacheDirectory.appendingPathComponent("share", isDirectory: true)
|
||||||
|
try FileManager.default.createDirectory(at: shareDirectory, withIntermediateDirectories: true)
|
||||||
|
let shareFile = shareDirectory.appendingPathComponent(name)
|
||||||
|
try write(to: shareFile, atomically: true, encoding: .utf8)
|
||||||
|
return shareFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@available(iOS 16.0, macOS 13.0, *)
|
@available(iOS 16.0, macOS 13.0, *)
|
||||||
public struct TypedProfile: Transferable, Codable {
|
public struct TypedProfile: Transferable, Codable {
|
||||||
public let content: LibboxProfileContent
|
public let content: LibboxProfileContent
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ extension SharedPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public nonisolated func set(_ newValue: T) async {
|
public nonisolated func set(_ newValue: T?) async {
|
||||||
do {
|
do {
|
||||||
try await SharedPreferences.write(name, newValue)
|
try await SharedPreferences.write(name, newValue)
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -4,25 +4,54 @@ public enum SharedPreferences {
|
|||||||
public static let selectedProfileID = Preference<Int64>("selected_profile_id", defaultValue: -1)
|
public static let selectedProfileID = Preference<Int64>("selected_profile_id", defaultValue: -1)
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
private static let disableMemoryLimitByDefault = true
|
private static let ignoreMemoryLimitByDefault = true
|
||||||
#else
|
#else
|
||||||
private static let disableMemoryLimitByDefault = false
|
private static let ignoreMemoryLimitByDefault = false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public static let disableMemoryLimit = Preference<Bool>("disable_memory_limit", defaultValue: disableMemoryLimitByDefault)
|
public static let alwaysOn = Preference<Bool>("always_on", defaultValue: false)
|
||||||
|
|
||||||
|
public static let ignoreMemoryLimit = Preference<Bool>("ignore_memory_limit", defaultValue: ignoreMemoryLimitByDefault)
|
||||||
|
public static let ignoreDeviceSleep = Preference<Bool>("ignore_device_sleep", defaultValue: false)
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
public static let excludeLocalNetworksByDefault = true
|
||||||
|
#elseif os(macOS)
|
||||||
|
public static let excludeLocalNetworksByDefault = false
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
public static let includeAllNetworks = Preference<Bool>("include_all_networks", defaultValue: false)
|
public static let includeAllNetworks = Preference<Bool>("include_all_networks", defaultValue: false)
|
||||||
|
public static let excludeAPNs = Preference<Bool>("exclude_apns", defaultValue: true)
|
||||||
|
public static let excludeLocalNetworks = Preference<Bool>("exclude_local_networks", defaultValue: excludeLocalNetworksByDefault)
|
||||||
|
public static let excludeCellularServices = Preference<Bool>("exclude_celluar_services", defaultValue: true)
|
||||||
|
public static let enforceRoutes = Preference<Bool>("enforce_routes", defaultValue: false)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
public static func resetPacketTunnel() async {
|
||||||
|
await ignoreMemoryLimit.set(nil)
|
||||||
|
await ignoreDeviceSleep.set(nil)
|
||||||
|
#if !os(tvOS)
|
||||||
|
await includeAllNetworks.set(nil)
|
||||||
|
await excludeAPNs.set(nil)
|
||||||
|
await excludeLocalNetworks.set(nil)
|
||||||
|
await excludeCellularServices.set(nil)
|
||||||
|
await enforceRoutes.set(nil)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public static let maxLogLines = Preference<Int>("max_log_lines", defaultValue: 300)
|
public static let maxLogLines = Preference<Int>("max_log_lines", defaultValue: 300)
|
||||||
public static let alwaysOn = Preference<Bool>("always_on", defaultValue: false)
|
|
||||||
public static let ignoreDeviceSleep = Preference<Bool>("ignore_device_sleep", defaultValue: false)
|
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
public static let showMenuBarExtra = Preference<Bool>("show_menu_bar_extra", defaultValue: true)
|
public static let showMenuBarExtra = Preference<Bool>("show_menu_bar_extra", defaultValue: true)
|
||||||
public static let menuBarExtraInBackground = Preference<Bool>("menu_bar_extra_in_background", defaultValue: false)
|
public static let menuBarExtraInBackground = Preference<Bool>("menu_bar_extra_in_background", defaultValue: false)
|
||||||
public static let startedByUser = Preference<Bool>("started_by_user", defaultValue: false)
|
public static let startedByUser = Preference<Bool>("started_by_user", defaultValue: false)
|
||||||
|
|
||||||
|
public static func resetMacOS() async {
|
||||||
|
await showMenuBarExtra.set(nil)
|
||||||
|
await menuBarExtraInBackground.set(nil)
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@@ -30,4 +59,20 @@ public enum SharedPreferences {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public static let systemProxyEnabled = Preference<Bool>("system_proxy_enabled", defaultValue: true)
|
public static let systemProxyEnabled = Preference<Bool>("system_proxy_enabled", defaultValue: true)
|
||||||
|
|
||||||
|
// Profile Override
|
||||||
|
|
||||||
|
public static let excludeDefaultRoute = Preference<Bool>("exclude_default_route", defaultValue: false)
|
||||||
|
public static let autoRouteUseSubRangesByDefault = Preference<Bool>("auto_route_use_sub_ranges_by_default", defaultValue: false)
|
||||||
|
|
||||||
|
public static func resetProfileOverride() async {
|
||||||
|
await excludeDefaultRoute.set(nil)
|
||||||
|
await autoRouteUseSubRangesByDefault.set(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
public static let inDebug = true
|
||||||
|
#else
|
||||||
|
public static let inDebug = false
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ public class ExtensionEnvironments: ObservableObject {
|
|||||||
@Published public var logClient = CommandClient(.log)
|
@Published public var logClient = CommandClient(.log)
|
||||||
@Published public var extensionProfileLoading = true
|
@Published public var extensionProfileLoading = true
|
||||||
@Published public var extensionProfile: ExtensionProfile?
|
@Published public var extensionProfile: ExtensionProfile?
|
||||||
|
|
||||||
public let profileUpdate = ObjectWillChangePublisher()
|
public let profileUpdate = ObjectWillChangePublisher()
|
||||||
public let selectedProfileUpdate = ObjectWillChangePublisher()
|
public let selectedProfileUpdate = ObjectWillChangePublisher()
|
||||||
|
public let openSettings = ObjectWillChangePublisher()
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
|||||||
throw NSError(domain: "nil return pointer", code: 0)
|
throw NSError(domain: "nil return pointer", code: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let autoRouteUseSubRangesByDefault = await SharedPreferences.autoRouteUseSubRangesByDefault.get()
|
||||||
|
|
||||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
|
||||||
if options.getAutoRoute() {
|
if options.getAutoRoute() {
|
||||||
settings.mtu = NSNumber(value: options.getMTU())
|
settings.mtu = NSNumber(value: options.getMTU())
|
||||||
@@ -57,6 +59,15 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
|||||||
let ipv4RoutePrefix = inet4RouteAddressIterator.next()!
|
let ipv4RoutePrefix = inet4RouteAddressIterator.next()!
|
||||||
ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask()))
|
ipv4Routes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask()))
|
||||||
}
|
}
|
||||||
|
} else if autoRouteUseSubRangesByDefault {
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "1.0.0.0", subnetMask: "255.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "2.0.0.0", subnetMask: "254.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "4.0.0.0", subnetMask: "252.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "8.0.0.0", subnetMask: "248.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "16.0.0.0", subnetMask: "240.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "32.0.0.0", subnetMask: "224.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "64.0.0.0", subnetMask: "192.0.0.0"))
|
||||||
|
ipv4Routes.append(NEIPv4Route(destinationAddress: "128.0.0.0", subnetMask: "128.0.0.0"))
|
||||||
} else {
|
} else {
|
||||||
ipv4Routes.append(NEIPv4Route.default())
|
ipv4Routes.append(NEIPv4Route.default())
|
||||||
}
|
}
|
||||||
@@ -66,6 +77,13 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
|||||||
let ipv4RoutePrefix = inet4RouteExcludeAddressIterator.next()!
|
let ipv4RoutePrefix = inet4RouteExcludeAddressIterator.next()!
|
||||||
ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask()))
|
ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: ipv4RoutePrefix.address(), subnetMask: ipv4RoutePrefix.mask()))
|
||||||
}
|
}
|
||||||
|
if await SharedPreferences.excludeDefaultRoute.get(), !ipv4Routes.isEmpty {
|
||||||
|
if !ipv4ExcludeRoutes.contains(where: { it in
|
||||||
|
it.destinationAddress == "0.0.0.0" && it.destinationSubnetMask == "255.255.255.254"
|
||||||
|
}) {
|
||||||
|
ipv4ExcludeRoutes.append(NEIPv4Route(destinationAddress: "0.0.0.0", subnetMask: "255.255.255.254"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ipv4Settings.includedRoutes = ipv4Routes
|
ipv4Settings.includedRoutes = ipv4Routes
|
||||||
ipv4Settings.excludedRoutes = ipv4ExcludeRoutes
|
ipv4Settings.excludedRoutes = ipv4ExcludeRoutes
|
||||||
@@ -89,6 +107,15 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
|
|||||||
let ipv6RoutePrefix = inet6RouteAddressIterator.next()!
|
let ipv6RoutePrefix = inet6RouteAddressIterator.next()!
|
||||||
ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix())))
|
ipv6Routes.append(NEIPv6Route(destinationAddress: ipv6RoutePrefix.address(), networkPrefixLength: NSNumber(value: ipv6RoutePrefix.prefix())))
|
||||||
}
|
}
|
||||||
|
} else if autoRouteUseSubRangesByDefault {
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "100::", networkPrefixLength: 8))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "200::", networkPrefixLength: 7))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "400::", networkPrefixLength: 6))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "800::", networkPrefixLength: 5))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "1000::", networkPrefixLength: 4))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "2000::", networkPrefixLength: 3))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "4000::", networkPrefixLength: 2))
|
||||||
|
ipv6Routes.append(NEIPv6Route(destinationAddress: "8000::", networkPrefixLength: 1))
|
||||||
} else {
|
} else {
|
||||||
ipv6Routes.append(NEIPv6Route.default())
|
ipv6Routes.append(NEIPv6Route.default())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ open class ExtensionProvider: NEPacketTunnelProvider {
|
|||||||
writeError("(packet-tunnel) redirect stderr error: \(error.localizedDescription)")
|
writeError("(packet-tunnel) redirect stderr error: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
|
|
||||||
await LibboxSetMemoryLimit(!SharedPreferences.disableMemoryLimit.get())
|
await LibboxSetMemoryLimit(!SharedPreferences.ignoreMemoryLimit.get())
|
||||||
ignoreDeviceSleep = await SharedPreferences.ignoreDeviceSleep.get()
|
ignoreDeviceSleep = await SharedPreferences.ignoreDeviceSleep.get()
|
||||||
|
|
||||||
if platformInterface == nil {
|
if platformInterface == nil {
|
||||||
|
|||||||
@@ -111,13 +111,13 @@
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func install(forceUpdate: Bool = false, inBackground: Bool = false) async throws -> OSSystemExtensionRequest.Result? {
|
public nonisolated static func install(forceUpdate: Bool = false, inBackground: Bool = false) async throws -> OSSystemExtensionRequest.Result? {
|
||||||
try await Task.detached {
|
try await Task.detached {
|
||||||
try SystemExtension(forceUpdate, inBackground).activation()
|
try SystemExtension(forceUpdate, inBackground).activation()
|
||||||
}.result.get()
|
}.result.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func uninstall() async throws -> OSSystemExtensionRequest.Result? {
|
public nonisolated static func uninstall() async throws -> OSSystemExtensionRequest.Result? {
|
||||||
try await Task.detached {
|
try await Task.detached {
|
||||||
try SystemExtension().deactivation()
|
try SystemExtension().deactivation()
|
||||||
}.result.get()
|
}.result.get()
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
#if canImport(UIKit)
|
||||||
|
import UIKit
|
||||||
|
#elseif canImport(AppKit)
|
||||||
|
import AppKit
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public extension Color {
|
||||||
|
static var textColor: Color {
|
||||||
|
#if canImport(UIKit)
|
||||||
|
return Color(uiColor: .label)
|
||||||
|
#elseif canImport(AppKit)
|
||||||
|
return Color(nsColor: .textColor)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static var linkColor: Color {
|
||||||
|
#if canImport(UIKit)
|
||||||
|
return Color(uiColor: .link)
|
||||||
|
#elseif canImport(AppKit)
|
||||||
|
return Color(nsColor: .linkColor)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ open class ApplicationDelegate: NSObject, NSApplicationDelegate {
|
|||||||
let launchedAsLogInItem =
|
let launchedAsLogInItem =
|
||||||
event?.eventID == kAEOpenApplication &&
|
event?.eventID == kAEOpenApplication &&
|
||||||
event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem
|
event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem
|
||||||
if !launchedAsLogInItem || !SharedPreferences.showMenuBarExtra.getBlocking() || !SharedPreferences.menuBarExtraInBackground.getBlocking() {
|
if SharedPreferences.inDebug || !launchedAsLogInItem || !SharedPreferences.showMenuBarExtra.getBlocking() || !SharedPreferences.menuBarExtraInBackground.getBlocking() {
|
||||||
NSApp.setActivationPolicy(.regular)
|
NSApp.setActivationPolicy(.regular)
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
} else {
|
} else {
|
||||||
@@ -35,7 +35,7 @@ open class ApplicationDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
|
public func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
|
||||||
!SharedPreferences.menuBarExtraInBackground.getBlocking()
|
SharedPreferences.inDebug || !SharedPreferences.menuBarExtraInBackground.getBlocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func applicationShouldHandleReopen(_: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
public func applicationShouldHandleReopen(_: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||||
|
|||||||
@@ -35,27 +35,14 @@ public struct MacApplication: Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SidebarCommands()
|
SidebarCommands()
|
||||||
|
CommandGroup(replacing: .appSettings) {
|
||||||
|
Button("Settings") {
|
||||||
|
environments.openSettings.send()
|
||||||
|
}
|
||||||
|
.keyboardShortcut(",", modifiers: [.command])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowGroup("New Profile", id: NewProfileView.windowID, for: NewProfileView.ImportRequest.self) { importRequest in
|
|
||||||
NewProfileView(importRequest.wrappedValue)
|
|
||||||
.environmentObject(environments)
|
|
||||||
}.commandsRemoved()
|
|
||||||
|
|
||||||
WindowGroup("Edit Profile", id: EditProfileWindowView.windowID, for: Int64.self) { profileID in
|
|
||||||
EditProfileWindowView(profileID.wrappedValue)
|
|
||||||
.environmentObject(environments)
|
|
||||||
}.commandsRemoved()
|
|
||||||
|
|
||||||
WindowGroup("Edit Content", id: EditProfileContentView.windowID, for: EditProfileContentView.Context.self) { context in
|
|
||||||
EditProfileContentView(context.wrappedValue)
|
|
||||||
.environmentObject(environments)
|
|
||||||
}.commandsRemoved()
|
|
||||||
|
|
||||||
Window("Service Log", id: ServiceLogView.windowID) {
|
|
||||||
ServiceLogView()
|
|
||||||
.environmentObject(environments)
|
|
||||||
}
|
|
||||||
MenuBarExtra(isInserted: $showMenuBarExtra) {
|
MenuBarExtra(isInserted: $showMenuBarExtra) {
|
||||||
MenuView(isMenuPresented: $isMenuPresented)
|
MenuView(isMenuPresented: $isMenuPresented)
|
||||||
.environmentObject(environments)
|
.environmentObject(environments)
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ public struct MainView: View {
|
|||||||
NavigationSplitView {
|
NavigationSplitView {
|
||||||
SidebarView()
|
SidebarView()
|
||||||
} detail: {
|
} detail: {
|
||||||
selection.contentView
|
NavigationStack {
|
||||||
.navigationTitle(selection.title)
|
selection.contentView
|
||||||
|
.navigationTitle(selection.title)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
environments.postReload()
|
environments.postReload()
|
||||||
@@ -55,6 +57,9 @@ public struct MainView: View {
|
|||||||
environments.connectLog()
|
environments.connectLog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onReceive(environments.openSettings) {
|
||||||
|
selection = .settings
|
||||||
|
}
|
||||||
.formStyle(.grouped)
|
.formStyle(.grouped)
|
||||||
.environment(\.selection, $selection)
|
.environment(\.selection, $selection)
|
||||||
.environment(\.importProfile, $importProfile)
|
.environment(\.importProfile, $importProfile)
|
||||||
|
|||||||
@@ -25,24 +25,30 @@ public struct SidebarView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
if extensionProfile.status.isConnectedStrict {
|
viewBuilder {
|
||||||
List(selection: selection) {
|
if extensionProfile.status.isConnectedStrict {
|
||||||
Section(NavigationPage.dashboard.title) {
|
List(selection: selection) {
|
||||||
Label("Overview", systemImage: "text.and.command.macwindow").tag(NavigationPage.dashboard)
|
Section(NavigationPage.dashboard.title) {
|
||||||
NavigationPage.groups.label.tag(NavigationPage.groups)
|
Label("Overview", systemImage: "text.and.command.macwindow")
|
||||||
|
.tint(.textColor)
|
||||||
|
.tag(NavigationPage.dashboard)
|
||||||
|
NavigationPage.groups.label.tag(NavigationPage.groups)
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
ForEach(NavigationPage.macosDefaultPages, id: \.self) { it in
|
||||||
|
it.label
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Divider()
|
} else {
|
||||||
ForEach(NavigationPage.macosDefaultPages, id: \.self) { it in
|
List(NavigationPage.allCases.filter { it in
|
||||||
|
it.visible(extensionProfile)
|
||||||
|
}, selection: selection) { it in
|
||||||
it.label
|
it.label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
List(NavigationPage.allCases.filter { it in
|
|
||||||
it.visible(extensionProfile)
|
|
||||||
}, selection: selection) { it in
|
|
||||||
it.label
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.listStyle(.sidebar)
|
||||||
|
.scrollDisabled(true)
|
||||||
}
|
}
|
||||||
.onChangeCompat(of: extensionProfile.status) {
|
.onChangeCompat(of: extensionProfile.status) {
|
||||||
if !selection.wrappedValue.visible(extensionProfile) {
|
if !selection.wrappedValue.visible(extensionProfile) {
|
||||||
|
|||||||
@@ -25,7 +25,10 @@
|
|||||||
3A2EAEED2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */; };
|
3A2EAEED2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */; };
|
||||||
3A3AA7FC2A4EFDAE002F78AB /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
3A3AA7FC2A4EFDAE002F78AB /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
||||||
3A3AA7FF2A4EFDB3002F78AB /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
3A3AA7FF2A4EFDB3002F78AB /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
||||||
|
3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3AB2A62B70C146001815AE /* CoreView.swift */; };
|
||||||
|
3A3AB2A92B70C5F1001815AE /* RequestReviewButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */; };
|
||||||
3A3DEBEB2A4FFE2D00373BF4 /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */; };
|
3A3DEBEB2A4FFE2D00373BF4 /* AppIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */; };
|
||||||
|
3A411CEC2B734959000D9501 /* MacAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A411CEB2B734959000D9501 /* MacAppView.swift */; };
|
||||||
3A44BB822A4DC28700E4C9F8 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A44BB812A4DC28700E4C9F8 /* MainView.swift */; };
|
3A44BB822A4DC28700E4C9F8 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A44BB812A4DC28700E4C9F8 /* MainView.swift */; };
|
||||||
3A4A020D2B53E3DC004EFB87 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = 3A4A020C2B53E3DC004EFB87 /* QRCode */; };
|
3A4A020D2B53E3DC004EFB87 /* QRCode in Frameworks */ = {isa = PBXBuildFile; productRef = 3A4A020C2B53E3DC004EFB87 /* QRCode */; };
|
||||||
3A4EAD1B2A4FEB02005435B3 /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
3A4EAD1B2A4FEB02005435B3 /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
||||||
@@ -37,15 +40,13 @@
|
|||||||
3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */; };
|
3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */; };
|
||||||
3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */; };
|
3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */; };
|
||||||
3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF12A4AF59900900CC8 /* ActiveDashboardView.swift */; };
|
3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF12A4AF59900900CC8 /* ActiveDashboardView.swift */; };
|
||||||
3A4EAD292A4FEB6D005435B3 /* Formtem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342D32A4AADB2002B34AC /* Formtem.swift */; };
|
3A4EAD292A4FEB6D005435B3 /* FormItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342D32A4AADB2002B34AC /* FormItem.swift */; };
|
||||||
3A4EAD2A2A4FEB6D005435B3 /* Binding+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */; };
|
3A4EAD2A2A4FEB6D005435B3 /* Binding+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */; };
|
||||||
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7E90302A46745A00D53052 /* ViewBuilder.swift */; };
|
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7E90302A46745A00D53052 /* ViewBuilder.swift */; };
|
||||||
3A4EAD2C2A4FEB77005435B3 /* EditProfileWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DFC2A4B096000900CC8 /* EditProfileWindowView.swift */; };
|
|
||||||
3A4EAD2D2A4FEB77005435B3 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */; };
|
3A4EAD2D2A4FEB77005435B3 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */; };
|
||||||
3A4EAD2E2A4FEB77005435B3 /* EditProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */; };
|
3A4EAD2E2A4FEB77005435B3 /* EditProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */; };
|
||||||
3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */; };
|
3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */; };
|
||||||
3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */; };
|
3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */; };
|
||||||
3A4EAD312A4FEB7B005435B3 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E712A4BF6F6009757F1 /* SettingView.swift */; };
|
|
||||||
3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */; };
|
3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */; };
|
||||||
3A4EAD342A4FEB7F005435B3 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA1ABB92A4C4054000FD4BA /* LogView.swift */; };
|
3A4EAD342A4FEB7F005435B3 /* LogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA1ABB92A4C4054000FD4BA /* LogView.swift */; };
|
||||||
3A4EAD352A4FEB9C005435B3 /* UIProfileUpdateTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A55F9572A4D137E003C4EF4 /* UIProfileUpdateTask.swift */; };
|
3A4EAD352A4FEB9C005435B3 /* UIProfileUpdateTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A55F9572A4D137E003C4EF4 /* UIProfileUpdateTask.swift */; };
|
||||||
@@ -62,6 +63,9 @@
|
|||||||
3A57DF422A4D927A00690BC5 /* Profile+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */; };
|
3A57DF422A4D927A00690BC5 /* Profile+Hashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */; };
|
||||||
3A5F26C82A503D4A00C27EDF /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
3A5F26C82A503D4A00C27EDF /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
|
||||||
3A5F26C92A503D4A00C27EDF /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
3A5F26C92A503D4A00C27EDF /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
3A60CC272B70880100D2D682 /* PacketTunnelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A60CC262B70880100D2D682 /* PacketTunnelView.swift */; };
|
||||||
|
3A60CC292B70A7C400D2D682 /* Color+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A60CC282B70A7C400D2D682 /* Color+Extension.swift */; };
|
||||||
|
3A60CC2B2B70AD6700D2D682 /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A60CC2A2B70AD6700D2D682 /* SettingView.swift */; };
|
||||||
3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A648D2C2A4EEAA600D95A12 /* Library.swift */; };
|
3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A648D2C2A4EEAA600D95A12 /* Library.swift */; };
|
||||||
3A648D542A4EF4C700D95A12 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF342B12A4AA520002B34AC /* NetworkExtension.framework */; };
|
3A648D542A4EF4C700D95A12 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF342B12A4AA520002B34AC /* NetworkExtension.framework */; };
|
||||||
3A6CA5A02A71317A0027933B /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3A6CA59F2A71317A0027933B /* MarkdownUI */; };
|
3A6CA5A02A71317A0027933B /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3A6CA59F2A71317A0027933B /* MarkdownUI */; };
|
||||||
@@ -73,6 +77,7 @@
|
|||||||
3A76504C2A4F08BA003945C5 /* Libbox.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC20DB2A4599D000A63465 /* Libbox.xcframework */; };
|
3A76504C2A4F08BA003945C5 /* Libbox.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC20DB2A4599D000A63465 /* Libbox.xcframework */; };
|
||||||
3A7701702A4E6B34008F031F /* IntentsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A77016F2A4E6B34008F031F /* IntentsExtension.swift */; };
|
3A7701702A4E6B34008F031F /* IntentsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A77016F2A4E6B34008F031F /* IntentsExtension.swift */; };
|
||||||
3A7701722A4E6B34008F031F /* Intents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7701712A4E6B34008F031F /* Intents.swift */; };
|
3A7701722A4E6B34008F031F /* Intents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7701712A4E6B34008F031F /* Intents.swift */; };
|
||||||
|
3A7904502B6E7BAC006C08D5 /* SponsorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A79044F2B6E7BAC006C08D5 /* SponsorView.swift */; };
|
||||||
3A7E90352A46756300D53052 /* SharedPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7E90342A46756300D53052 /* SharedPreferences.swift */; };
|
3A7E90352A46756300D53052 /* SharedPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7E90342A46756300D53052 /* SharedPreferences.swift */; };
|
||||||
3A7E90382A46778E00D53052 /* BinaryCodable in Frameworks */ = {isa = PBXBuildFile; productRef = 3A7E90372A46778E00D53052 /* BinaryCodable */; };
|
3A7E90382A46778E00D53052 /* BinaryCodable in Frameworks */ = {isa = PBXBuildFile; productRef = 3A7E90372A46778E00D53052 /* BinaryCodable */; };
|
||||||
3A8655142A4FA26600B7181F /* IntentsExtension.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
3A8655142A4FA26600B7181F /* IntentsExtension.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
@@ -142,6 +147,11 @@
|
|||||||
3AEECC4F2A6DFFE1006A0E0C /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC210D2A459B1900A63465 /* MainView.swift */; };
|
3AEECC4F2A6DFFE1006A0E0C /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC210D2A459B1900A63465 /* MainView.swift */; };
|
||||||
3AF342A02A4A9916002B34AC /* ExtensionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF3429F2A4A9916002B34AC /* ExtensionProfile.swift */; };
|
3AF342A02A4A9916002B34AC /* ExtensionProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF3429F2A4A9916002B34AC /* ExtensionProfile.swift */; };
|
||||||
3AF3A3D22B2207F3001FD7C1 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */; };
|
3AF3A3D22B2207F3001FD7C1 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */; };
|
||||||
|
3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */; };
|
||||||
|
3AF5E3E92B6F9CFB0058B9E8 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF5E3E82B6F9CFB0058B9E8 /* StoreKit.framework */; };
|
||||||
|
3AF5E3EB2B6F9D020058B9E8 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF5E3EA2B6F9D020058B9E8 /* StoreKit.framework */; };
|
||||||
|
3AF5E3EC2B6F9D070058B9E8 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF5E3EA2B6F9D020058B9E8 /* StoreKit.framework */; };
|
||||||
|
3AF5E3EE2B6F9D0F0058B9E8 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF5E3ED2B6F9D0F0058B9E8 /* StoreKit.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -445,8 +455,11 @@
|
|||||||
3A27D8FF2A89BE230031EBCC /* CommandClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandClient.swift; sourceTree = "<group>"; };
|
3A27D8FF2A89BE230031EBCC /* CommandClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandClient.swift; sourceTree = "<group>"; };
|
||||||
3A27D9012A89C6870031EBCC /* ExtensionEnvironments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionEnvironments.swift; sourceTree = "<group>"; };
|
3A27D9012A89C6870031EBCC /* ExtensionEnvironments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionEnvironments.swift; sourceTree = "<group>"; };
|
||||||
3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndependentApplicationDelegate.swift; sourceTree = "<group>"; };
|
3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndependentApplicationDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
3A3AB2A62B70C146001815AE /* CoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreView.swift; sourceTree = "<group>"; };
|
||||||
|
3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestReviewButton.swift; sourceTree = "<group>"; };
|
||||||
3A3DEBE12A4FFA1A00373BF4 /* ExtensionFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExtensionFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/ExtensionFoundation.framework; sourceTree = DEVELOPER_DIR; };
|
3A3DEBE12A4FFA1A00373BF4 /* ExtensionFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExtensionFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/ExtensionFoundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppIntents.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/AppIntents.framework; sourceTree = DEVELOPER_DIR; };
|
3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppIntents.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk/System/Library/Frameworks/AppIntents.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
3A411CEB2B734959000D9501 /* MacAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppView.swift; sourceTree = "<group>"; };
|
||||||
3A44BB662A4DBF7900E4C9F8 /* SFI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SFI.entitlements; sourceTree = "<group>"; };
|
3A44BB662A4DBF7900E4C9F8 /* SFI.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SFI.entitlements; sourceTree = "<group>"; };
|
||||||
3A44BB802A4DC25E00E4C9F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
3A44BB802A4DC25E00E4C9F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
3A44BB812A4DC28700E4C9F8 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
|
3A44BB812A4DC28700E4C9F8 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
|
||||||
@@ -463,6 +476,9 @@
|
|||||||
3A57DF362A4D5D2600690BC5 /* Profile+Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Date.swift"; sourceTree = "<group>"; };
|
3A57DF362A4D5D2600690BC5 /* Profile+Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Date.swift"; sourceTree = "<group>"; };
|
||||||
3A57DF3F2A4D70B600690BC5 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; };
|
3A57DF3F2A4D70B600690BC5 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; };
|
||||||
3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Hashable.swift"; sourceTree = "<group>"; };
|
3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Hashable.swift"; sourceTree = "<group>"; };
|
||||||
|
3A60CC262B70880100D2D682 /* PacketTunnelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelView.swift; sourceTree = "<group>"; };
|
||||||
|
3A60CC282B70A7C400D2D682 /* Color+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
3A60CC2A2B70AD6700D2D682 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = "<group>"; };
|
||||||
3A648D2C2A4EEAA600D95A12 /* Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Library.swift; sourceTree = "<group>"; };
|
3A648D2C2A4EEAA600D95A12 /* Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Library.swift; sourceTree = "<group>"; };
|
||||||
3A6CA5A52A713AA10027933B /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = "<group>"; };
|
3A6CA5A52A713AA10027933B /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = "<group>"; };
|
||||||
3A77016D2A4E6B34008F031F /* IntentsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = IntentsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
3A77016D2A4E6B34008F031F /* IntentsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = IntentsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -470,6 +486,7 @@
|
|||||||
3A7701712A4E6B34008F031F /* Intents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Intents.swift; sourceTree = "<group>"; };
|
3A7701712A4E6B34008F031F /* Intents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Intents.swift; sourceTree = "<group>"; };
|
||||||
3A7701732A4E6B34008F031F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
3A7701732A4E6B34008F031F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
3A7701802A4E71F5008F031F /* IntentsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentsExtension.entitlements; sourceTree = "<group>"; };
|
3A7701802A4E71F5008F031F /* IntentsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IntentsExtension.entitlements; sourceTree = "<group>"; };
|
||||||
|
3A79044F2B6E7BAC006C08D5 /* SponsorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorView.swift; sourceTree = "<group>"; };
|
||||||
3A7E90302A46745A00D53052 /* ViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewBuilder.swift; sourceTree = "<group>"; };
|
3A7E90302A46745A00D53052 /* ViewBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewBuilder.swift; sourceTree = "<group>"; };
|
||||||
3A7E90342A46756300D53052 /* SharedPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedPreferences.swift; sourceTree = "<group>"; };
|
3A7E90342A46756300D53052 /* SharedPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedPreferences.swift; sourceTree = "<group>"; };
|
||||||
3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShadredPreferences+Database.swift"; sourceTree = "<group>"; };
|
3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ShadredPreferences+Database.swift"; sourceTree = "<group>"; };
|
||||||
@@ -477,7 +494,6 @@
|
|||||||
3A99B42B2A75288C0010D4B0 /* ViewCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewCompat.swift; sourceTree = "<group>"; };
|
3A99B42B2A75288C0010D4B0 /* ViewCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewCompat.swift; sourceTree = "<group>"; };
|
||||||
3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationDestinationCompat.swift; sourceTree = "<group>"; };
|
3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationDestinationCompat.swift; sourceTree = "<group>"; };
|
||||||
3AA1ABB92A4C4054000FD4BA /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = "<group>"; };
|
3AA1ABB92A4C4054000FD4BA /* LogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogView.swift; sourceTree = "<group>"; };
|
||||||
3AAB5E712A4BF6F6009757F1 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = "<group>"; };
|
|
||||||
3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLogView.swift; sourceTree = "<group>"; };
|
3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLogView.swift; sourceTree = "<group>"; };
|
||||||
3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileContentView.swift; sourceTree = "<group>"; };
|
3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileContentView.swift; sourceTree = "<group>"; };
|
||||||
3AAB5E7A2A4C1446009757F1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
3AAB5E7A2A4C1446009757F1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@@ -502,7 +518,6 @@
|
|||||||
3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
|
3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
|
||||||
3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
||||||
3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewProfileView.swift; sourceTree = "<group>"; };
|
3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewProfileView.swift; sourceTree = "<group>"; };
|
||||||
3ADF8DFC2A4B096000900CC8 /* EditProfileWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileWindowView.swift; sourceTree = "<group>"; };
|
|
||||||
3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileView.swift; sourceTree = "<group>"; };
|
3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileView.swift; sourceTree = "<group>"; };
|
||||||
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Unwrap.swift"; sourceTree = "<group>"; };
|
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Unwrap.swift"; sourceTree = "<group>"; };
|
||||||
3AE171992A8128DD00393060 /* TVExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TVExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
3AE171992A8128DD00393060 /* TVExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TVExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -550,8 +565,12 @@
|
|||||||
3AF342B12A4AA520002B34AC /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
3AF342B12A4AA520002B34AC /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||||
3AF342CC2A4AA88C002B34AC /* StartStopButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartStopButton.swift; sourceTree = "<group>"; };
|
3AF342CC2A4AA88C002B34AC /* StartStopButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartStopButton.swift; sourceTree = "<group>"; };
|
||||||
3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionStatusView.swift; sourceTree = "<group>"; };
|
3AF342D02A4AACC4002B34AC /* ExtensionStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionStatusView.swift; sourceTree = "<group>"; };
|
||||||
3AF342D32A4AADB2002B34AC /* Formtem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formtem.swift; sourceTree = "<group>"; };
|
3AF342D32A4AADB2002B34AC /* FormItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormItem.swift; sourceTree = "<group>"; };
|
||||||
3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS17.0.sdk/usr/lib/libresolv.tbd; sourceTree = DEVELOPER_DIR; };
|
3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS17.0.sdk/usr/lib/libresolv.tbd; sourceTree = DEVELOPER_DIR; };
|
||||||
|
3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileOverrideView.swift; sourceTree = "<group>"; };
|
||||||
|
3AF5E3E82B6F9CFB0058B9E8 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.2.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
3AF5E3EA2B6F9D020058B9E8 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
|
||||||
|
3AF5E3ED2B6F9D0F0058B9E8 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS17.2.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -587,6 +606,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
3AF5E3EE2B6F9D0F0058B9E8 /* StoreKit.framework in Frameworks */,
|
||||||
3A4FB1572A73467F007012B9 /* Library.framework in Frameworks */,
|
3A4FB1572A73467F007012B9 /* Library.framework in Frameworks */,
|
||||||
3A4FB15C2A73468C007012B9 /* ApplicationLibrary.framework in Frameworks */,
|
3A4FB15C2A73468C007012B9 /* ApplicationLibrary.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -605,6 +625,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
3AF5E3E92B6F9CFB0058B9E8 /* StoreKit.framework in Frameworks */,
|
||||||
3A9759202A4EB69C00E4404B /* Library.framework in Frameworks */,
|
3A9759202A4EB69C00E4404B /* Library.framework in Frameworks */,
|
||||||
3A4EAD372A4FEC20005435B3 /* ApplicationLibrary.framework in Frameworks */,
|
3A4EAD372A4FEC20005435B3 /* ApplicationLibrary.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -617,6 +638,7 @@
|
|||||||
3A5F26C82A503D4A00C27EDF /* Library.framework in Frameworks */,
|
3A5F26C82A503D4A00C27EDF /* Library.framework in Frameworks */,
|
||||||
3AEECC352A6DFDAD006A0E0C /* MacLibrary.framework in Frameworks */,
|
3AEECC352A6DFDAD006A0E0C /* MacLibrary.framework in Frameworks */,
|
||||||
3AEECC412A6DFE29006A0E0C /* MacLibrary.framework in Frameworks */,
|
3AEECC412A6DFE29006A0E0C /* MacLibrary.framework in Frameworks */,
|
||||||
|
3AF5E3EB2B6F9D020058B9E8 /* StoreKit.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -644,6 +666,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
3AF5E3EC2B6F9D070058B9E8 /* StoreKit.framework in Frameworks */,
|
||||||
3AEECC1D2A6DFA79006A0E0C /* Library.framework in Frameworks */,
|
3AEECC1D2A6DFA79006A0E0C /* Library.framework in Frameworks */,
|
||||||
3AEECC3A2A6DFDC5006A0E0C /* MacLibrary.framework in Frameworks */,
|
3AEECC3A2A6DFDC5006A0E0C /* MacLibrary.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -733,8 +756,13 @@
|
|||||||
3AAB5E702A4BF6EA009757F1 /* Setting */ = {
|
3AAB5E702A4BF6EA009757F1 /* Setting */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3AAB5E712A4BF6F6009757F1 /* SettingView.swift */,
|
|
||||||
3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */,
|
3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */,
|
||||||
|
3A79044F2B6E7BAC006C08D5 /* SponsorView.swift */,
|
||||||
|
3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */,
|
||||||
|
3A60CC262B70880100D2D682 /* PacketTunnelView.swift */,
|
||||||
|
3A60CC2A2B70AD6700D2D682 /* SettingView.swift */,
|
||||||
|
3A3AB2A62B70C146001815AE /* CoreView.swift */,
|
||||||
|
3A411CEB2B734959000D9501 /* MacAppView.swift */,
|
||||||
);
|
);
|
||||||
path = Setting;
|
path = Setting;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -767,7 +795,6 @@
|
|||||||
3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */,
|
3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */,
|
||||||
3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */,
|
3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */,
|
||||||
3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */,
|
3ADF8DF82A4AFCB400900CC8 /* NewProfileView.swift */,
|
||||||
3ADF8DFC2A4B096000900CC8 /* EditProfileWindowView.swift */,
|
|
||||||
3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */,
|
3ADF8E002A4B0F6300900CC8 /* EditProfileView.swift */,
|
||||||
3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */,
|
3AC8CF9A2A736C750002AF3C /* ImportProfileView.swift */,
|
||||||
);
|
);
|
||||||
@@ -839,6 +866,9 @@
|
|||||||
3AEC21012A459AE300A63465 /* Frameworks */ = {
|
3AEC21012A459AE300A63465 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3AF5E3ED2B6F9D0F0058B9E8 /* StoreKit.framework */,
|
||||||
|
3AF5E3EA2B6F9D020058B9E8 /* StoreKit.framework */,
|
||||||
|
3AF5E3E82B6F9CFB0058B9E8 /* StoreKit.framework */,
|
||||||
3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */,
|
3AF3A3D12B2207E1001FD7C1 /* libresolv.tbd */,
|
||||||
3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */,
|
3A3DEBE62A4FFA6000373BF4 /* AppIntents.framework */,
|
||||||
3A3DEBE12A4FFA1A00373BF4 /* ExtensionFoundation.framework */,
|
3A3DEBE12A4FFA1A00373BF4 /* ExtensionFoundation.framework */,
|
||||||
@@ -911,6 +941,7 @@
|
|||||||
3AEC21472A45A9DE00A63465 /* Bundle+Version.swift */,
|
3AEC21472A45A9DE00A63465 /* Bundle+Version.swift */,
|
||||||
3AEC214B2A45AA8E00A63465 /* FilePath.swift */,
|
3AEC214B2A45AA8E00A63465 /* FilePath.swift */,
|
||||||
3A2223552A6E1BDE00C50B23 /* Variant.swift */,
|
3A2223552A6E1BDE00C50B23 /* Variant.swift */,
|
||||||
|
3A60CC282B70A7C400D2D682 /* Color+Extension.swift */,
|
||||||
);
|
);
|
||||||
path = Shared;
|
path = Shared;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -988,7 +1019,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3A7E90302A46745A00D53052 /* ViewBuilder.swift */,
|
3A7E90302A46745A00D53052 /* ViewBuilder.swift */,
|
||||||
3AF342D32A4AADB2002B34AC /* Formtem.swift */,
|
3AF342D32A4AADB2002B34AC /* FormItem.swift */,
|
||||||
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */,
|
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */,
|
||||||
3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */,
|
3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */,
|
||||||
3AB1220A2A70FD500087CD55 /* Alert.swift */,
|
3AB1220A2A70FD500087CD55 /* Alert.swift */,
|
||||||
@@ -997,6 +1028,7 @@
|
|||||||
3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */,
|
3A99B42D2A752ABB0010D4B0 /* NavigationDestinationCompat.swift */,
|
||||||
3AC729F12A76088E00FE8EC1 /* ShareButton.swift */,
|
3AC729F12A76088E00FE8EC1 /* ShareButton.swift */,
|
||||||
3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */,
|
3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */,
|
||||||
|
3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */,
|
||||||
);
|
);
|
||||||
path = Abstract;
|
path = Abstract;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1442,15 +1474,20 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
3A60CC2B2B70AD6700D2D682 /* SettingView.swift in Sources */,
|
||||||
3A99B42A2A7526990010D4B0 /* NavigationStackCompat.swift in Sources */,
|
3A99B42A2A7526990010D4B0 /* NavigationStackCompat.swift in Sources */,
|
||||||
3A4EAD2E2A4FEB77005435B3 /* EditProfileView.swift in Sources */,
|
3A4EAD2E2A4FEB77005435B3 /* EditProfileView.swift in Sources */,
|
||||||
3A4EAD2A2A4FEB6D005435B3 /* Binding+Unwrap.swift in Sources */,
|
3A4EAD2A2A4FEB6D005435B3 /* Binding+Unwrap.swift in Sources */,
|
||||||
3A4EAD2D2A4FEB77005435B3 /* ProfileView.swift in Sources */,
|
3A4EAD2D2A4FEB77005435B3 /* ProfileView.swift in Sources */,
|
||||||
3A4EAD352A4FEB9C005435B3 /* UIProfileUpdateTask.swift in Sources */,
|
3A4EAD352A4FEB9C005435B3 /* UIProfileUpdateTask.swift in Sources */,
|
||||||
|
3A60CC272B70880100D2D682 /* PacketTunnelView.swift in Sources */,
|
||||||
3A4EAD222A4FEB54005435B3 /* NavigationPage.swift in Sources */,
|
3A4EAD222A4FEB54005435B3 /* NavigationPage.swift in Sources */,
|
||||||
|
3A411CEC2B734959000D9501 /* MacAppView.swift in Sources */,
|
||||||
3AC8CF9B2A736C750002AF3C /* ImportProfileView.swift in Sources */,
|
3AC8CF9B2A736C750002AF3C /* ImportProfileView.swift in Sources */,
|
||||||
3A4EAD292A4FEB6D005435B3 /* Formtem.swift in Sources */,
|
3A4EAD292A4FEB6D005435B3 /* FormItem.swift in Sources */,
|
||||||
|
3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */,
|
||||||
3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */,
|
3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */,
|
||||||
|
3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */,
|
||||||
3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */,
|
3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */,
|
||||||
3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */,
|
3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */,
|
||||||
3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */,
|
3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */,
|
||||||
@@ -1458,7 +1495,6 @@
|
|||||||
3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */,
|
3A4EAD282A4FEB65005435B3 /* ActiveDashboardView.swift in Sources */,
|
||||||
3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */,
|
3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */,
|
||||||
3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */,
|
3A4EAD322A4FEB7B005435B3 /* ServiceLogView.swift in Sources */,
|
||||||
3A4EAD312A4FEB7B005435B3 /* SettingView.swift in Sources */,
|
|
||||||
3A1CF2F82A50F0A5000A8289 /* GroupItemView.swift in Sources */,
|
3A1CF2F82A50F0A5000A8289 /* GroupItemView.swift in Sources */,
|
||||||
3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */,
|
3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */,
|
||||||
3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */,
|
3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */,
|
||||||
@@ -1468,8 +1504,8 @@
|
|||||||
3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */,
|
3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */,
|
||||||
3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */,
|
3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */,
|
||||||
3A4EAD212A4FEB3C005435B3 /* ApplicationLibrary.swift in Sources */,
|
3A4EAD212A4FEB3C005435B3 /* ApplicationLibrary.swift in Sources */,
|
||||||
|
3A7904502B6E7BAC006C08D5 /* SponsorView.swift in Sources */,
|
||||||
3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */,
|
3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */,
|
||||||
3A4EAD2C2A4FEB77005435B3 /* EditProfileWindowView.swift in Sources */,
|
|
||||||
3A1CF2FA2A50F0BD000A8289 /* OutboundGroupItem.swift in Sources */,
|
3A1CF2FA2A50F0BD000A8289 /* OutboundGroupItem.swift in Sources */,
|
||||||
3A99B42E2A752ABB0010D4B0 /* NavigationDestinationCompat.swift in Sources */,
|
3A99B42E2A752ABB0010D4B0 /* NavigationDestinationCompat.swift in Sources */,
|
||||||
3AC729F22A76088E00FE8EC1 /* ShareButton.swift in Sources */,
|
3AC729F22A76088E00FE8EC1 /* ShareButton.swift in Sources */,
|
||||||
@@ -1480,6 +1516,7 @@
|
|||||||
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */,
|
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */,
|
||||||
3AC5EC082A6417470077AF34 /* DeviceCensorship.swift in Sources */,
|
3AC5EC082A6417470077AF34 /* DeviceCensorship.swift in Sources */,
|
||||||
3A0C6D3E2A79D6A600A4DF2B /* DashboardPage.swift in Sources */,
|
3A0C6D3E2A79D6A600A4DF2B /* DashboardPage.swift in Sources */,
|
||||||
|
3A3AB2A92B70C5F1001815AE /* RequestReviewButton.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1547,6 +1584,7 @@
|
|||||||
3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */,
|
3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */,
|
||||||
3AEC21452A45A93800A63465 /* HTTPClient.swift in Sources */,
|
3AEC21452A45A93800A63465 /* HTTPClient.swift in Sources */,
|
||||||
3AEC213C2A459FDF00A63465 /* Databse.swift in Sources */,
|
3AEC213C2A459FDF00A63465 /* Databse.swift in Sources */,
|
||||||
|
3A60CC292B70A7C400D2D682 /* Color+Extension.swift in Sources */,
|
||||||
3A4EAD3C2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift in Sources */,
|
3A4EAD3C2A4FECCE005435B3 /* NEVPNStatus+isConnected.swift in Sources */,
|
||||||
3ADBB4252A7389640041D44F /* ProfileServer.swift in Sources */,
|
3ADBB4252A7389640041D44F /* ProfileServer.swift in Sources */,
|
||||||
3AEC21422A45A8FF00A63465 /* Profile+Update.swift in Sources */,
|
3AEC21422A45A8FF00A63465 /* Profile+Update.swift in Sources */,
|
||||||
@@ -1974,7 +2012,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||||
PRODUCT_NAME = "sing-box";
|
PRODUCT_NAME = "sing-box";
|
||||||
SDKROOT = appletvos;
|
SDKROOT = appletvos;
|
||||||
@@ -2008,7 +2046,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||||
PRODUCT_NAME = "sing-box";
|
PRODUCT_NAME = "sing-box";
|
||||||
SDKROOT = appletvos;
|
SDKROOT = appletvos;
|
||||||
@@ -2246,7 +2284,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0;
|
||||||
OTHER_CODE_SIGN_FLAGS = "--deep";
|
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||||
PRODUCT_NAME = "sing-box";
|
PRODUCT_NAME = "sing-box";
|
||||||
@@ -2286,7 +2324,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0;
|
||||||
OTHER_CODE_SIGN_FLAGS = "--deep";
|
OTHER_CODE_SIGN_FLAGS = "--deep";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||||
PRODUCT_NAME = "sing-box";
|
PRODUCT_NAME = "sing-box";
|
||||||
@@ -2309,7 +2347,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 192;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = Z56Z6NYZN2;
|
DEVELOPMENT_TEAM = Z56Z6NYZN2;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -2325,7 +2363,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0;
|
||||||
OTHER_CODE_SIGN_FLAGS = "";
|
OTHER_CODE_SIGN_FLAGS = "";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||||
PRODUCT_NAME = "sing-box";
|
PRODUCT_NAME = "sing-box";
|
||||||
@@ -2347,7 +2385,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 192;
|
CURRENT_PROJECT_VERSION = 195;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = Z56Z6NYZN2;
|
DEVELOPMENT_TEAM = Z56Z6NYZN2;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -2363,7 +2401,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0;
|
||||||
OTHER_CODE_SIGN_FLAGS = "";
|
OTHER_CODE_SIGN_FLAGS = "";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
|
||||||
PRODUCT_NAME = "sing-box";
|
PRODUCT_NAME = "sing-box";
|
||||||
@@ -2505,7 +2543,7 @@
|
|||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0-alpha.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
||||||
PRODUCT_NAME = "$(inherited)";
|
PRODUCT_NAME = "$(inherited)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2541,7 +2579,7 @@
|
|||||||
"@executable_path/../../../../Frameworks",
|
"@executable_path/../../../../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0-alpha.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
|
||||||
PRODUCT_NAME = "$(inherited)";
|
PRODUCT_NAME = "$(inherited)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2581,7 +2619,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0-alpha.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
||||||
PRODUCT_NAME = SFM;
|
PRODUCT_NAME = SFM;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2620,7 +2658,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.8.4;
|
MARKETING_VERSION = 1.9.0-alpha.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
|
||||||
PRODUCT_NAME = SFM;
|
PRODUCT_NAME = SFM;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
Reference in New Issue
Block a user