@@ -0,0 +1,12 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public extension Bool {
|
||||||
|
func toString() -> String {
|
||||||
|
if self {
|
||||||
|
return String(localized: "true")
|
||||||
|
} else {
|
||||||
|
return String(localized: "false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,8 +20,8 @@ public struct ConnectionDetailsView: View {
|
|||||||
if let closedAt = connection.closedAt {
|
if let closedAt = connection.closedAt {
|
||||||
FormTextItem("Closed At", closedAt.myFormat)
|
FormTextItem("Closed At", closedAt.myFormat)
|
||||||
}
|
}
|
||||||
FormTextItem("Upload", LibboxFormatBytes(connection.uploadTotal))
|
FormTextItem("Uplink", LibboxFormatBytes(connection.uploadTotal))
|
||||||
FormTextItem("Download", LibboxFormatBytes(connection.downloadTotal))
|
FormTextItem("Downlink", LibboxFormatBytes(connection.downloadTotal))
|
||||||
Section("Metadata") {
|
Section("Metadata") {
|
||||||
FormTextItem("Inbound", connection.inbound)
|
FormTextItem("Inbound", connection.inbound)
|
||||||
FormTextItem("Inbound Type", connection.inboundType)
|
FormTextItem("Inbound Type", connection.inboundType)
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ public extension DashboardPage {
|
|||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .overview:
|
case .overview:
|
||||||
return NSLocalizedString("Overview", comment: "")
|
return String(localized: "Overview")
|
||||||
case .groups:
|
case .groups:
|
||||||
return NSLocalizedString("Groups", comment: "")
|
return String(localized: "Groups")
|
||||||
case .connections:
|
case .connections:
|
||||||
return NSLocalizedString("Connections", comment: "")
|
return String(localized: "Connections")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ public struct DashboardView: View {
|
|||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
alert = Alert(
|
alert = Alert(
|
||||||
title: Text("Full Disk Access permission is required"),
|
title: Text("Full Disk Access permission is required"),
|
||||||
message: Text("Please grant the permission for SFMExtension, then we can continue."),
|
message: Text("Please grant the permission for **SFMExtension**, then we can continue."),
|
||||||
primaryButton: .default(Text("Authorize"), action: openFDASettings),
|
primaryButton: .default(Text("Authorize"), action: openFDASettings),
|
||||||
secondaryButton: .cancel()
|
secondaryButton: .cancel()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,49 +27,49 @@ public struct ExtensionStatusView: View {
|
|||||||
VStack {
|
VStack {
|
||||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columnCount), alignment: .leading) {
|
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: columnCount), alignment: .leading) {
|
||||||
if ApplicationLibrary.inPreview {
|
if ApplicationLibrary.inPreview {
|
||||||
StatusItem("Status") {
|
StatusItem(String(localized: "Status")) {
|
||||||
StatusLine("Memory", "6.4 MB")
|
StatusLine(String(localized: "Memory"), "6.4 MB")
|
||||||
StatusLine("Goroutines", "89")
|
StatusLine(String(localized: "Goroutines"), "89")
|
||||||
}
|
}
|
||||||
StatusItem("Connections") {
|
StatusItem(String(localized: "Connections")) {
|
||||||
StatusLine("Inbound", "34")
|
StatusLine(String(localized: "Inbound"), "34")
|
||||||
StatusLine("Outbound", "28")
|
StatusLine(String(localized: "Outbound"), "28")
|
||||||
}
|
}
|
||||||
StatusItem("Traffic") {
|
StatusItem(String(localized: "Traffic")) {
|
||||||
StatusLine("Uplink", "38 B/s")
|
StatusLine(String(localized: "Uplink"), "38 B/s")
|
||||||
StatusLine("Downlink", "249 MB/s")
|
StatusLine(String(localized: "Downlink"), "249 MB/s")
|
||||||
}
|
}
|
||||||
StatusItem("TrafficTotal") {
|
StatusItem(String(localized: "Traffic Total")) {
|
||||||
StatusLine("Uplink", "52 MB")
|
StatusLine(String(localized: "Uplink"), "52 MB")
|
||||||
StatusLine("Downlink", "5.6 GB")
|
StatusLine(String(localized: "Downlink"), "5.6 GB")
|
||||||
}
|
}
|
||||||
} else if let message = commandClient.status {
|
} else if let message = commandClient.status {
|
||||||
StatusItem("Status") {
|
StatusItem(String(localized: "Status")) {
|
||||||
StatusLine("Memory", LibboxFormatMemoryBytes(message.memory))
|
StatusLine(String(localized: "Memory"), LibboxFormatMemoryBytes(message.memory))
|
||||||
StatusLine("Goroutines", "\(message.goroutines)")
|
StatusLine(String(localized: "Goroutines"), "\(message.goroutines)")
|
||||||
}
|
}
|
||||||
StatusItem("Connections") {
|
StatusItem(String(localized: "Connections")) {
|
||||||
StatusLine("Inbound", "\(message.connectionsIn)")
|
StatusLine(String(localized: "Inbound"), "\(message.connectionsIn)")
|
||||||
StatusLine("Outbound", "\(message.connectionsOut)")
|
StatusLine(String(localized: "Outbound"), "\(message.connectionsOut)")
|
||||||
}
|
}
|
||||||
if message.trafficAvailable {
|
if message.trafficAvailable {
|
||||||
StatusItem("Traffic") {
|
StatusItem(String(localized: "Traffic")) {
|
||||||
StatusLine("Uplink", "\(LibboxFormatBytes(message.uplink))/s")
|
StatusLine(String(localized: "Uplink"), "\(LibboxFormatBytes(message.uplink))/s")
|
||||||
StatusLine("Downlink", "\(LibboxFormatBytes(message.downlink))/s")
|
StatusLine(String(localized: "Downlink"), "\(LibboxFormatBytes(message.downlink))/s")
|
||||||
}
|
}
|
||||||
StatusItem("Traffic Total") {
|
StatusItem(String(localized: "Traffic Total")) {
|
||||||
StatusLine("Uplink", LibboxFormatBytes(message.uplinkTotal))
|
StatusLine(String(localized: "Uplink"), LibboxFormatBytes(message.uplinkTotal))
|
||||||
StatusLine("Downlink", LibboxFormatBytes(message.downlinkTotal))
|
StatusLine(String(localized: "Downlink"), LibboxFormatBytes(message.downlinkTotal))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
StatusItem("Status") {
|
StatusItem(String(localized: "Status")) {
|
||||||
StatusLine("Memory", "...")
|
StatusLine(String(localized: "Memory"), "...")
|
||||||
StatusLine("Goroutines", "...")
|
StatusLine(String(localized: "Goroutines"), "...")
|
||||||
}
|
}
|
||||||
StatusItem("Connections") {
|
StatusItem(String(localized: "Connections")) {
|
||||||
StatusLine("Inbound", "...")
|
StatusLine(String(localized: "Inbound"), "...")
|
||||||
StatusLine("Outbound", "...")
|
StatusLine(String(localized: "Outbound"), "...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.background {
|
}.background {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
do {
|
do {
|
||||||
if let result = try await SystemExtension.install() {
|
if let result = try await SystemExtension.install() {
|
||||||
if result == .willCompleteAfterReboot {
|
if result == .willCompleteAfterReboot {
|
||||||
alert = Alert(errorMessage: "Need Reboot")
|
alert = Alert(errorMessage: String(localized: "Need Reboot"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await callback()
|
await callback()
|
||||||
|
|||||||
@@ -32,19 +32,19 @@ public extension NavigationPage {
|
|||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .dashboard:
|
case .dashboard:
|
||||||
return NSLocalizedString("Dashboard", comment: "")
|
return String(localized: "Dashboard")
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
case .groups:
|
case .groups:
|
||||||
return NSLocalizedString("Groups", comment: "")
|
return String(localized: "Groups")
|
||||||
case .connections:
|
case .connections:
|
||||||
return NSLocalizedString("Connections", comment: "")
|
return NSLocalizedString("Connections", comment: "")
|
||||||
#endif
|
#endif
|
||||||
case .logs:
|
case .logs:
|
||||||
return NSLocalizedString("Logs", comment: "")
|
return String(localized: "Logs")
|
||||||
case .profiles:
|
case .profiles:
|
||||||
return NSLocalizedString("Profiles", comment: "")
|
return String(localized: "Profiles")
|
||||||
case .settings:
|
case .settings:
|
||||||
return NSLocalizedString("Settings", comment: "")
|
return String(localized: "Settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,9 +104,9 @@
|
|||||||
|
|
||||||
private var navigationTitle: String {
|
private var navigationTitle: String {
|
||||||
if readOnly {
|
if readOnly {
|
||||||
return "View Content"
|
return String(localized: "View Content")
|
||||||
} else {
|
} else {
|
||||||
return "Edit Content"
|
return String(localized: "Edit Content")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public struct EditProfileView: View {
|
|||||||
public init() {}
|
public init() {}
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
FormView {
|
FormView {
|
||||||
FormItem("Name") {
|
FormItem(String(localized: "Name")) {
|
||||||
TextField("Name", text: $profile.name, prompt: Text("Required"))
|
TextField("Name", text: $profile.name, prompt: Text("Required"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
@@ -31,17 +31,17 @@ public struct EditProfileView: View {
|
|||||||
}
|
}
|
||||||
.disabled(true)
|
.disabled(true)
|
||||||
if profile.type == .icloud {
|
if profile.type == .icloud {
|
||||||
FormItem("Path") {
|
FormItem(String(localized: "Path")) {
|
||||||
TextField("Path", text: $profile.path, prompt: Text("Required"))
|
TextField("Path", text: $profile.path, prompt: Text("Required"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
} else if profile.type == .remote {
|
} else if profile.type == .remote {
|
||||||
FormItem("URL") {
|
FormItem(String(localized: "URL")) {
|
||||||
TextField("URL", text: $profile.remoteURL.unwrapped(""), prompt: Text("Required"))
|
TextField("URL", text: $profile.remoteURL.unwrapped(""), prompt: Text("Required"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
Toggle("Auto Update", isOn: $profile.autoUpdate)
|
Toggle("Auto Update", isOn: $profile.autoUpdate)
|
||||||
FormItem("Auto Update Interval") {
|
FormItem(String(localized: "Auto Update Interval")) {
|
||||||
TextField("Auto Update Interval", text: $profile.autoUpdateInterval.stringBinding(defaultValue: 60), prompt: Text("In Minutes"))
|
TextField("Auto Update Interval", text: $profile.autoUpdateInterval.stringBinding(defaultValue: 60), prompt: Text("In Minutes"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public struct NewProfileView: View {
|
|||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
FormView {
|
FormView {
|
||||||
FormItem("Name") {
|
FormItem(String(localized: "Name")) {
|
||||||
TextField("Name", text: $profileName, prompt: Text("Required"))
|
TextField("Name", text: $profileName, prompt: Text("Required"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
@@ -76,17 +76,17 @@ public struct NewProfileView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if profileType == .icloud {
|
} else if profileType == .icloud {
|
||||||
FormItem("Path") {
|
FormItem(String(localized: "Path")) {
|
||||||
TextField("Path", text: $remotePath, prompt: Text("Required"))
|
TextField("Path", text: $remotePath, prompt: Text("Required"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
} else if profileType == .remote {
|
} else if profileType == .remote {
|
||||||
FormItem("URL") {
|
FormItem(String(localized: "URL")) {
|
||||||
TextField("URL", text: $remotePath, prompt: Text("Required"))
|
TextField("URL", text: $remotePath, prompt: Text("Required"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
}
|
}
|
||||||
Toggle("Auto Update", isOn: $autoUpdate)
|
Toggle("Auto Update", isOn: $autoUpdate)
|
||||||
FormItem("Auto Update Interval") {
|
FormItem(String(localized: "Auto Update Interval")) {
|
||||||
TextField("Auto Update Interval", text: $autoUpdateInterval.stringBinding(defaultValue: 60), prompt: Text("In Minutes"))
|
TextField("Auto Update Interval", text: $autoUpdateInterval.stringBinding(defaultValue: 60), prompt: Text("In Minutes"))
|
||||||
.multilineTextAlignment(.trailing)
|
.multilineTextAlignment(.trailing)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@@ -135,15 +135,15 @@ public struct NewProfileView: View {
|
|||||||
isSaving = false
|
isSaving = false
|
||||||
}
|
}
|
||||||
if profileName.isEmpty {
|
if profileName.isEmpty {
|
||||||
alert = Alert(errorMessage: "Missing profile name")
|
alert = Alert(errorMessage: String(localized: "Missing profile name"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if remotePath.isEmpty {
|
if remotePath.isEmpty {
|
||||||
if profileType == .icloud {
|
if profileType == .icloud {
|
||||||
alert = Alert(errorMessage: "Missing path")
|
alert = Alert(errorMessage: String(localized: "Missing path"))
|
||||||
return
|
return
|
||||||
} else if profileType == .remote {
|
} else if profileType == .remote {
|
||||||
alert = Alert(errorMessage: "Missing URL")
|
alert = Alert(errorMessage: String(localized: "Missing URL"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
|
|
||||||
|
import Library
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
import Library
|
|
||||||
import ServiceManagement
|
import ServiceManagement
|
||||||
import SwiftUI
|
#endif
|
||||||
|
|
||||||
public struct MacAppView: View {
|
public struct AppView: View {
|
||||||
@State private var isLoading = true
|
@State private var isLoading = true
|
||||||
|
|
||||||
@State private var startAtLogin = false
|
@State private var startAtLogin = false
|
||||||
@Environment(\.showMenuBarExtra) private var showMenuBarExtra
|
@Environment(\.showMenuBarExtra) private var showMenuBarExtra
|
||||||
@State private var menuBarExtraInBackground = false
|
@State private var menuBarExtraInBackground = false
|
||||||
|
|
||||||
@State private var alert: Alert?
|
@State private var alert: Alert?
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
viewBuilder {
|
viewBuilder {
|
||||||
if isLoading {
|
if isLoading {
|
||||||
ProgressView().onAppear {
|
ProgressView().onAppear {
|
||||||
Task {
|
Task {
|
||||||
await loadSettings()
|
await loadSettings()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
FormView {
|
} else {
|
||||||
|
FormView {
|
||||||
|
#if os(macOS)
|
||||||
FormToggle("Start At Login", "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.", $startAtLogin) { newValue in
|
FormToggle("Start At Login", "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.", $startAtLogin) { newValue in
|
||||||
updateLoginItems(newValue)
|
updateLoginItems(newValue)
|
||||||
}
|
}
|
||||||
@@ -66,21 +69,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alertBinding($alert)
|
|
||||||
.navigationTitle("App")
|
|
||||||
#if os(iOS)
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
.alertBinding($alert)
|
||||||
|
.navigationTitle("App")
|
||||||
|
#if os(iOS)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
private func loadSettings() async {
|
private func loadSettings() async {
|
||||||
|
#if os(macOS)
|
||||||
startAtLogin = SMAppService.mainApp.status == .enabled
|
startAtLogin = SMAppService.mainApp.status == .enabled
|
||||||
menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get()
|
menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get()
|
||||||
isLoading = false
|
#endif
|
||||||
}
|
isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
|
||||||
private func updateLoginItems(_ startAtLogin: Bool) {
|
private func updateLoginItems(_ startAtLogin: Bool) {
|
||||||
do {
|
do {
|
||||||
@@ -143,6 +151,6 @@
|
|||||||
alert = Alert(error)
|
alert = Alert(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -87,11 +87,11 @@ public struct ServiceLogView: View {
|
|||||||
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
||||||
return identifier + String(UnicodeScalar(UInt8(value)))
|
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||||
}
|
}
|
||||||
var deviceInfo = "Machine: " + machineName + "\n"
|
var deviceInfo = String("Machine: ") + machineName + "\n"
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
await deviceInfo += "System: " + (UIDevice.current.systemName) + " " + (UIDevice.current.systemVersion) + "\n"
|
await deviceInfo += String("System: ") + (UIDevice.current.systemName) + " " + (UIDevice.current.systemVersion) + "\n"
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
deviceInfo += "System: macOS " + ProcessInfo().operatingSystemVersionString + "\n"
|
deviceInfo += String("System: ") + "macOS " + ProcessInfo().operatingSystemVersionString + "\n"
|
||||||
#endif
|
#endif
|
||||||
content = deviceInfo + "\n" + content
|
content = deviceInfo + "\n" + content
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,18 @@ public struct SettingView: View {
|
|||||||
switch self {
|
switch self {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
case .app:
|
case .app:
|
||||||
return NSLocalizedString("App", comment: "")
|
return String(localized: "App")
|
||||||
#endif
|
#endif
|
||||||
case .core:
|
case .core:
|
||||||
return NSLocalizedString("Core", comment: "")
|
return String(localized: "Core")
|
||||||
case .packetTunnel:
|
case .packetTunnel:
|
||||||
return NSLocalizedString("Packet Tunnel", comment: "")
|
return String(localized: "Packet Tunnel")
|
||||||
case .onDemandRules:
|
case .onDemandRules:
|
||||||
return NSLocalizedString("On Demand Rules", comment: "")
|
return String(localized: "On Demand Rules")
|
||||||
case .profileOverride:
|
case .profileOverride:
|
||||||
return NSLocalizedString("Profile Override", comment: "")
|
return String(localized: "Profile Override")
|
||||||
case .sponsors:
|
case .sponsors:
|
||||||
return NSLocalizedString("Sponsors", comment: "")
|
return String(localized: "Sponsors")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ public struct SettingView: View {
|
|||||||
switch self {
|
switch self {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
case .app:
|
case .app:
|
||||||
MacAppView()
|
AppView()
|
||||||
#endif
|
#endif
|
||||||
case .core:
|
case .core:
|
||||||
CoreView()
|
CoreView()
|
||||||
@@ -106,11 +106,29 @@ public struct SettingView: View {
|
|||||||
}
|
}
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
Section("About") {
|
Section("About") {
|
||||||
Link(destination: URL(string: "https://sing-box.sagernet.org/")!) {
|
Link(destination: URL(string: String(localized: "https://sing-box.sagernet.org/"))!) {
|
||||||
Label("Documentation", systemImage: "doc.on.doc.fill")
|
Label("Documentation", systemImage: "doc.on.doc.fill")
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.foregroundColor(.accentColor)
|
.foregroundColor(.accentColor)
|
||||||
|
.contextMenu {
|
||||||
|
Link(destination: URL(string: String(localized: "https://sing-box.sagernet.org/changelog/"))!) {
|
||||||
|
Text("Changelog")
|
||||||
|
}
|
||||||
|
Link(destination: URL(string: String(localized: "https://sing-box.sagernet.org/configuration/"))!) {
|
||||||
|
Text("Configuration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Link(destination: URL(string: String("https://github.com/SagerNet/sing-box"))!) {
|
||||||
|
Label("Source Code", systemImage: "pills.fill")
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.foregroundColor(.accentColor)
|
||||||
|
.contextMenu {
|
||||||
|
Link(destination: URL(string: String("https://github.com/SagerNet/sing-box/releases"))!) {
|
||||||
|
Text("Releases")
|
||||||
|
}
|
||||||
|
}
|
||||||
RequestReviewButton {
|
RequestReviewButton {
|
||||||
Label("Rate on the App Store", systemImage: "text.bubble.fill")
|
Label("Rate on the App Store", systemImage: "text.bubble.fill")
|
||||||
}
|
}
|
||||||
@@ -145,7 +163,7 @@ public struct SettingView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text(taiwanFlagAvailable.description)
|
Text(taiwanFlagAvailable.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ public struct SponsorsView: View {
|
|||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
FormButton("GitHub Sponsor (recommended)") {
|
FormButton(String(localized: "GitHub Sponsors (recommended)")) {
|
||||||
openURL(URL(string: "https://github.com/sponsors/nekohasekai")!)
|
openURL(URL(string: "https://github.com/sponsors/nekohasekai")!)
|
||||||
}
|
}
|
||||||
FormButton("Other methods") {
|
FormButton(String(localized: "Other methods")) {
|
||||||
openURL(URL(string: "https://sekai.icu/sponsors/")!)
|
openURL(URL(string: "https://sekai.icu/sponsors/")!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum SharedPreferences {
|
public enum SharedPreferences {
|
||||||
|
public static let language = Preference<String>("language", defaultValue: "")
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public class HTTPClient {
|
|||||||
userAgent += Bundle.main.versionNumber
|
userAgent += Bundle.main.versionNumber
|
||||||
userAgent += "; sing-box "
|
userAgent += "; sing-box "
|
||||||
userAgent += LibboxVersion()
|
userAgent += LibboxVersion()
|
||||||
|
userAgent += "; language "
|
||||||
|
userAgent += Locale.current.identifier
|
||||||
userAgent += ")"
|
userAgent += ")"
|
||||||
return userAgent
|
return userAgent
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ open class ApplicationDelegate: NSObject, NSApplicationDelegate, UNUserNotificat
|
|||||||
public func applicationDidFinishLaunching(_: Notification) {
|
public func applicationDidFinishLaunching(_: Notification) {
|
||||||
NSLog("Here I stand")
|
NSLog("Here I stand")
|
||||||
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false)
|
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false)
|
||||||
|
LibboxSetLocale(Locale.current.identifier)
|
||||||
let notificationCenter = UNUserNotificationCenter.current()
|
let notificationCenter = UNUserNotificationCenter.current()
|
||||||
notificationCenter.setNotificationCategories([
|
notificationCenter.setNotificationCategories([
|
||||||
UNNotificationCategory(
|
UNNotificationCategory(
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public struct MainView: View {
|
|||||||
await importURLProfile(url)
|
await importURLProfile(url)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert = Alert(errorMessage: "Handled unknown URL \(url.absoluteString)")
|
alert = Alert(errorMessage: String(localized: "Handled unknown URL \(url.absoluteString)"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ public struct MainView: View {
|
|||||||
if directoryName != "Applications" {
|
if directoryName != "Applications" {
|
||||||
alert = Alert(
|
alert = Alert(
|
||||||
title: Text("Wrong application location"),
|
title: Text("Wrong application location"),
|
||||||
message: Text("This app needs to be placed under ~/Applications to work."),
|
message: Text("This app needs to be placed under the Applications folder to work."),
|
||||||
dismissButton: .default(Text("Ok")) {
|
dismissButton: .default(Text("Ok")) {
|
||||||
NSWorkspace.shared.selectFile(Bundle.main.bundlePath, inFileViewerRootedAtPath: "")
|
NSWorkspace.shared.selectFile(Bundle.main.bundlePath, inFileViewerRootedAtPath: "")
|
||||||
NSApp.terminate(nil)
|
NSApp.terminate(nil)
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public struct MenuView: View {
|
|||||||
if profileList.isEmpty {
|
if profileList.isEmpty {
|
||||||
Text("Empty profiles")
|
Text("Empty profiles")
|
||||||
} else {
|
} else {
|
||||||
MenuSection("Profile")
|
MenuSection(String(localized: "Profile"))
|
||||||
Picker("", selection: selectedProfileIDLocal) {
|
Picker("", selection: selectedProfileIDLocal) {
|
||||||
ForEach(profileList, id: \.id) { profile in
|
ForEach(profileList, id: \.id) { profile in
|
||||||
Text(profile.name)
|
Text(profile.name)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class ApplicationDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCe
|
|||||||
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||||
NSLog("Here I stand")
|
NSLog("Here I stand")
|
||||||
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false)
|
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, false)
|
||||||
|
LibboxSetLocale(Locale.current.identifier)
|
||||||
let notificationCenter = UNUserNotificationCenter.current()
|
let notificationCenter = UNUserNotificationCenter.current()
|
||||||
notificationCenter.setNotificationCategories([
|
notificationCenter.setNotificationCategories([
|
||||||
UNNotificationCategory(
|
UNNotificationCategory(
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ struct MainView: View {
|
|||||||
selection = .profiles
|
selection = .profiles
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert = Alert(errorMessage: "Handled unknown URL \(url.absoluteString)")
|
alert = Alert(errorMessage: String(localized: "Handled unknown URL \(url.absoluteString)"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class ApplicationDelegate: NSObject, UIApplicationDelegate {
|
|||||||
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
||||||
NSLog("Here I stand")
|
NSLog("Here I stand")
|
||||||
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, true)
|
LibboxSetup(FilePath.sharedDirectory.relativePath, FilePath.workingDirectory.relativePath, FilePath.cacheDirectory.relativePath, true)
|
||||||
|
LibboxSetLocale(Locale.current.identifier)
|
||||||
setup()
|
setup()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
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 /* SponsorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A79044F2B6E7BAC006C08D5 /* SponsorsView.swift */; };
|
3A7904502B6E7BAC006C08D5 /* SponsorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A79044F2B6E7BAC006C08D5 /* SponsorsView.swift */; };
|
||||||
|
3A7C8B452D0C81E10070EE55 /* StringCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7C8B442D0C81DC0070EE55 /* StringCompat.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, ); }; };
|
||||||
@@ -164,6 +165,13 @@
|
|||||||
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 */; };
|
3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */; };
|
||||||
|
9C37F17D2C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
|
9C37F17E2C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
|
9C37F17F2C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
|
9C37F1812C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
|
9C37F1822C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
|
9C37F1832C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
|
9C37F1842C131100001B9FA8 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -521,6 +529,7 @@
|
|||||||
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 /* SponsorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorsView.swift; sourceTree = "<group>"; };
|
3A79044F2B6E7BAC006C08D5 /* SponsorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SponsorsView.swift; sourceTree = "<group>"; };
|
||||||
|
3A7C8B442D0C81DC0070EE55 /* StringCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringCompat.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>"; };
|
||||||
@@ -613,6 +622,7 @@
|
|||||||
3AF342D32A4AADB2002B34AC /* FormItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormItem.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>"; };
|
3AF5E3E62B6F90640058B9E8 /* ProfileOverrideView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileOverrideView.swift; sourceTree = "<group>"; };
|
||||||
|
9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -886,6 +896,7 @@
|
|||||||
3AEC20BC2A45991900A63465 = {
|
3AEC20BC2A45991900A63465 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9C37F17C2C131100001B9FA8 /* Localizable.xcstrings */,
|
||||||
3AEC20DB2A4599D000A63465 /* Libbox.xcframework */,
|
3AEC20DB2A4599D000A63465 /* Libbox.xcframework */,
|
||||||
3AEC20F42A459AB400A63465 /* SFI */,
|
3AEC20F42A459AB400A63465 /* SFI */,
|
||||||
3AEC210A2A459B1900A63465 /* SFM */,
|
3AEC210A2A459B1900A63465 /* SFM */,
|
||||||
@@ -1094,6 +1105,7 @@
|
|||||||
3AF342D22A4AADA5002B34AC /* Abstract */ = {
|
3AF342D22A4AADA5002B34AC /* Abstract */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3A7C8B442D0C81DC0070EE55 /* StringCompat.swift */,
|
||||||
3A7E90302A46745A00D53052 /* ViewBuilder.swift */,
|
3A7E90302A46745A00D53052 /* ViewBuilder.swift */,
|
||||||
3AF342D32A4AADB2002B34AC /* FormItem.swift */,
|
3AF342D32A4AADB2002B34AC /* FormItem.swift */,
|
||||||
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */,
|
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */,
|
||||||
@@ -1487,6 +1499,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
9C37F1832C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1494,6 +1507,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
9C37F1812C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
3AC03B9D2A72BF3500B7946F /* Assets.xcassets in Resources */,
|
3AC03B9D2A72BF3500B7946F /* Assets.xcassets in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1519,6 +1533,7 @@
|
|||||||
files = (
|
files = (
|
||||||
3A6CA5A92A713C420027933B /* AppIcon.icns in Resources */,
|
3A6CA5A92A713C420027933B /* AppIcon.icns in Resources */,
|
||||||
3AABFD432A9CC5A7005A24A4 /* Upload.plist in Resources */,
|
3AABFD432A9CC5A7005A24A4 /* Upload.plist in Resources */,
|
||||||
|
9C37F17D2C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
3AEC20FA2A459AB500A63465 /* Assets.xcassets in Resources */,
|
3AEC20FA2A459AB500A63465 /* Assets.xcassets in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1529,6 +1544,7 @@
|
|||||||
files = (
|
files = (
|
||||||
3A6CA5A72A713ABA0027933B /* AppIcon.icns in Resources */,
|
3A6CA5A72A713ABA0027933B /* AppIcon.icns in Resources */,
|
||||||
3A6CA5A32A713A580027933B /* Assets.xcassets in Resources */,
|
3A6CA5A32A713A580027933B /* Assets.xcassets in Resources */,
|
||||||
|
9C37F17E2C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
3A251C122A52D09700651082 /* Assets.xcassets in Resources */,
|
3A251C122A52D09700651082 /* Assets.xcassets in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1537,6 +1553,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
9C37F1822C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1552,6 +1569,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
3AABFD472A9CCC58005A24A4 /* Upload.plist in Resources */,
|
3AABFD472A9CCC58005A24A4 /* Upload.plist in Resources */,
|
||||||
|
9C37F17F2C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
3A6CA5A82A713B340027933B /* AppIcon.icns in Resources */,
|
3A6CA5A82A713B340027933B /* AppIcon.icns in Resources */,
|
||||||
3AEC8A4F2A6E5E18003702E1 /* Assets.xcassets in Resources */,
|
3AEC8A4F2A6E5E18003702E1 /* Assets.xcassets in Resources */,
|
||||||
3A2F29EB2C998A5D007E024C /* Export.plist in Resources */,
|
3A2F29EB2C998A5D007E024C /* Export.plist in Resources */,
|
||||||
@@ -1563,6 +1581,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
9C37F1842C131100001B9FA8 /* Localizable.xcstrings in Resources */,
|
||||||
3A6CA5A62A713AA10027933B /* AppIcon.icns in Resources */,
|
3A6CA5A62A713AA10027933B /* AppIcon.icns in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1626,6 +1645,7 @@
|
|||||||
3ACE6DE32ACADF55009D9A8A /* Binding+Setter.swift in Sources */,
|
3ACE6DE32ACADF55009D9A8A /* Binding+Setter.swift in Sources */,
|
||||||
3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */,
|
3A4EAD262A4FEB65005435B3 /* ExtensionStatusView.swift in Sources */,
|
||||||
3A63269E2C0DE12D0076E274 /* ConnectionListView.swift in Sources */,
|
3A63269E2C0DE12D0076E274 /* ConnectionListView.swift in Sources */,
|
||||||
|
3A7C8B452D0C81E10070EE55 /* StringCompat.swift in Sources */,
|
||||||
3A4EAD252A4FEB65005435B3 /* StartStopButton.swift in Sources */,
|
3A4EAD252A4FEB65005435B3 /* StartStopButton.swift in Sources */,
|
||||||
3A6326A02C0DE15C0076E274 /* Connection.swift in Sources */,
|
3A6326A02C0DE15C0076E274 /* Connection.swift in Sources */,
|
||||||
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */,
|
3A4EAD2B2A4FEB6D005435B3 /* ViewBuilder.swift in Sources */,
|
||||||
|
|||||||
Reference in New Issue
Block a user