fix: corrected loading of app with doubleclick on chat document
This commit is contained in:
@@ -14,6 +14,8 @@ struct ContentView: View {
|
||||
@State private var exportDocument: ChatExportDocument?
|
||||
@State private var documentErrorMessage: String?
|
||||
@State private var exportErrorMessage: String?
|
||||
@State private var startupTask: Task<Void, Never>?
|
||||
@State private var isOpeningDocument = false
|
||||
|
||||
private var isRunningTests: Bool {
|
||||
ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
|
||||
@@ -37,14 +39,9 @@ struct ContentView: View {
|
||||
if Preferences.apiAutoStart && !isRunningTests {
|
||||
vm.startAPIServer()
|
||||
}
|
||||
// Restore autosaved session if no document is being opened
|
||||
if !documentController.hasPendingOpenRequests && !isRunningTests {
|
||||
Task {
|
||||
await vm.restoreFromAutosave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduleStartupWork()
|
||||
processPendingOpenRequests()
|
||||
}
|
||||
.onChange(of: modelManager.currentModel) {
|
||||
@@ -62,6 +59,7 @@ struct ContentView: View {
|
||||
showLoadError = modelManager.errorMessage != nil
|
||||
}
|
||||
.onChange(of: documentController.openRequestNonce) {
|
||||
startupTask?.cancel()
|
||||
processPendingOpenRequests()
|
||||
}
|
||||
}
|
||||
@@ -376,11 +374,58 @@ struct ContentView: View {
|
||||
|
||||
Task {
|
||||
while let url = documentController.consumeNextOpenRequest() {
|
||||
startupTask?.cancel()
|
||||
await openDocument(at: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduleStartupWork() {
|
||||
guard let chatVM else { return }
|
||||
|
||||
startupTask?.cancel()
|
||||
startupTask = Task {
|
||||
try? await Task.sleep(nanoseconds: 250_000_000)
|
||||
guard !Task.isCancelled else { return }
|
||||
|
||||
if documentController.hasPendingOpenRequests {
|
||||
await MainActor.run {
|
||||
processPendingOpenRequests()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard !isOpeningDocument else { return }
|
||||
|
||||
if !isRunningTests, ChatViewModel.hasAutosavedSession {
|
||||
let restored = await chatVM.restoreFromAutosave()
|
||||
guard !Task.isCancelled else { return }
|
||||
guard !isOpeningDocument else { return }
|
||||
if restored || documentController.hasPendingOpenRequests {
|
||||
await MainActor.run {
|
||||
processPendingOpenRequests()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
guard !Task.isCancelled else { return }
|
||||
guard !isOpeningDocument else { return }
|
||||
guard !documentController.hasPendingOpenRequests else {
|
||||
await MainActor.run {
|
||||
processPendingOpenRequests()
|
||||
}
|
||||
return
|
||||
}
|
||||
guard modelManager.currentModel == nil else { return }
|
||||
|
||||
let modelId = Preferences.defaultModelId ?? Preferences.lastModelId ?? ModelConfig.default.id
|
||||
if let config = ModelConfig.availableModels.first(where: { $0.id == modelId }) {
|
||||
await modelManager.loadModel(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func openDocument(at url: URL, skipUnsavedCheck: Bool = false) async {
|
||||
if !skipUnsavedCheck {
|
||||
let shouldContinue = confirmDiscardUnsavedChanges(
|
||||
@@ -390,6 +435,10 @@ struct ContentView: View {
|
||||
guard shouldContinue else { return }
|
||||
}
|
||||
|
||||
startupTask?.cancel()
|
||||
isOpeningDocument = true
|
||||
defer { isOpeningDocument = false }
|
||||
|
||||
do {
|
||||
try await chatVM?.loadDocument(from: url)
|
||||
} catch {
|
||||
|
||||
@@ -46,15 +46,6 @@ struct MLXServerApp: App {
|
||||
.environment(documentController)
|
||||
.environment(modelManager)
|
||||
.environment(sceneStore)
|
||||
.task {
|
||||
guard !documentController.hasPendingOpenRequests else { return }
|
||||
guard !ChatViewModel.hasAutosavedSession else { return }
|
||||
// Auto-load: configured default → last used → built-in default
|
||||
let modelId = Preferences.defaultModelId ?? Preferences.lastModelId ?? ModelConfig.default.id
|
||||
if let config = ModelConfig.availableModels.first(where: { $0.id == modelId }) {
|
||||
await modelManager.loadModel(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
.windowStyle(.titleBar)
|
||||
.defaultSize(width: 800, height: 700)
|
||||
|
||||
@@ -34,6 +34,22 @@ final class ModelManager {
|
||||
|
||||
private var idleTimer: Timer?
|
||||
private(set) var lastUsed: Date?
|
||||
private var latestLoadRequestID = UUID()
|
||||
|
||||
private func clearLoadedState() {
|
||||
idleTimer?.invalidate()
|
||||
idleTimer = nil
|
||||
lastUsed = nil
|
||||
modelContainer = nil
|
||||
currentModel = nil
|
||||
isLoading = false
|
||||
isDownloading = false
|
||||
downloadProgress = 0
|
||||
loadingModelName = ""
|
||||
downloadFilesTotal = 0
|
||||
downloadFilesCompleted = 0
|
||||
downloadSpeed = 0
|
||||
}
|
||||
|
||||
/// Load a model, unloading the current one first.
|
||||
/// Prefers the local snapshot from ~/.cache/huggingface/hub/ (shared with the Python server).
|
||||
@@ -43,7 +59,10 @@ final class ModelManager {
|
||||
return // already loaded
|
||||
}
|
||||
|
||||
unloadModel()
|
||||
let requestID = UUID()
|
||||
latestLoadRequestID = requestID
|
||||
clearLoadedState()
|
||||
MLX.GPU.clearCache()
|
||||
isLoading = true
|
||||
downloadProgress = 0
|
||||
loadingModelName = config.displayName
|
||||
@@ -94,15 +113,18 @@ final class ModelManager {
|
||||
)
|
||||
}
|
||||
|
||||
guard latestLoadRequestID == requestID else { return }
|
||||
self.isDownloading = false
|
||||
self.modelContainer = container
|
||||
self.currentModel = config
|
||||
touchActivity()
|
||||
} catch {
|
||||
guard latestLoadRequestID == requestID else { return }
|
||||
self.isDownloading = false
|
||||
self.errorMessage = "Failed to load model: \(error.localizedDescription)"
|
||||
}
|
||||
|
||||
guard latestLoadRequestID == requestID else { return }
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
@@ -115,11 +137,8 @@ final class ModelManager {
|
||||
|
||||
/// Unload the current model and free GPU memory.
|
||||
func unloadModel() {
|
||||
idleTimer?.invalidate()
|
||||
idleTimer = nil
|
||||
lastUsed = nil
|
||||
modelContainer = nil
|
||||
currentModel = nil
|
||||
latestLoadRequestID = UUID()
|
||||
clearLoadedState()
|
||||
MLX.GPU.clearCache()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user