diff --git a/app/EXO/EXO/ExoProcessController.swift b/app/EXO/EXO/ExoProcessController.swift index a97c0a06..f978d0e0 100644 --- a/app/EXO/EXO/ExoProcessController.swift +++ b/app/EXO/EXO/ExoProcessController.swift @@ -4,6 +4,7 @@ import Foundation private let customNamespaceKey = "EXOCustomNamespace" private let hfTokenKey = "EXOHFToken" +private let hfEndpointKey = "EXOHFEndpoint" private let enableImageModelsKey = "EXOEnableImageModels" private let offlineModeKey = "EXOOfflineMode" private let onboardingCompletedKey = "EXOOnboardingCompleted" @@ -53,6 +54,14 @@ final class ExoProcessController: ObservableObject { UserDefaults.standard.set(hfToken, forKey: hfTokenKey) } } + @Published var hfEndpoint: String = { + return UserDefaults.standard.string(forKey: hfEndpointKey) ?? "" + }() + { + didSet { + UserDefaults.standard.set(hfEndpoint, forKey: hfEndpointKey) + } + } @Published var enableImageModels: Bool = { return UserDefaults.standard.bool(forKey: enableImageModelsKey) }() @@ -273,6 +282,9 @@ final class ExoProcessController: ObservableObject { if !hfToken.isEmpty { environment["HF_TOKEN"] = hfToken } + if !hfEndpoint.isEmpty { + environment["HF_ENDPOINT"] = hfEndpoint + } if enableImageModels { environment["EXO_ENABLE_IMAGE_MODELS"] = "true" } diff --git a/app/EXO/EXO/Views/SettingsView.swift b/app/EXO/EXO/Views/SettingsView.swift index bc600efe..64deae83 100644 --- a/app/EXO/EXO/Views/SettingsView.swift +++ b/app/EXO/EXO/Views/SettingsView.swift @@ -12,6 +12,7 @@ struct SettingsView: View { @State private var pendingNamespace: String = "" @State private var pendingHFToken: String = "" + @State private var pendingHFEndpoint: String = "" @State private var pendingEnableImageModels = false @State private var pendingOfflineMode = false @State private var needsRestart = false @@ -42,6 +43,7 @@ struct SettingsView: View { .onAppear { pendingNamespace = controller.customNamespace pendingHFToken = controller.hfToken + pendingHFEndpoint = controller.hfEndpoint pendingEnableImageModels = controller.enableImageModels pendingOfflineMode = controller.offlineMode needsRestart = false @@ -74,6 +76,17 @@ struct SettingsView: View { .foregroundColor(.secondary) } + Section { + LabeledContent("HuggingFace Endpoint") { + TextField("default", text: $pendingHFEndpoint) + .textFieldStyle(.roundedBorder) + .frame(width: 200) + } + Text("Defaults to huggingface.co. Use a mirror (e.g. hf-mirror.com) for China.") + .font(.caption) + .foregroundColor(.secondary) + } + Section { Toggle("Offline Mode", isOn: $pendingOfflineMode) Text("Skip internet checks and use only locally available models.") @@ -454,6 +467,7 @@ struct SettingsView: View { private var hasGeneralChanges: Bool { pendingNamespace != controller.customNamespace || pendingHFToken != controller.hfToken + || pendingHFEndpoint != controller.hfEndpoint || pendingOfflineMode != controller.offlineMode } @@ -464,6 +478,7 @@ struct SettingsView: View { private func applyGeneralSettings() { controller.customNamespace = pendingNamespace controller.hfToken = pendingHFToken + controller.hfEndpoint = pendingHFEndpoint controller.offlineMode = pendingOfflineMode restartIfRunning() } diff --git a/justfile b/justfile index f278e579..c3bab3c8 100644 --- a/justfile +++ b/justfile @@ -31,6 +31,10 @@ build-dashboard: package: uv run pyinstaller packaging/pyinstaller/exo.spec +build-app: package + xcodebuild build -project app/EXO/EXO.xcodeproj -scheme EXO -configuration Debug -derivedDataPath app/EXO/build + @echo "\nBuild complete. Run with:\n open {{justfile_directory()}}/app/EXO/build/Build/Products/Debug/EXO.app" + clean: rm -rf **/__pycache__ rm -rf target/