fix: corrected loading of app with doubleclick on chat document

This commit is contained in:
2026-03-21 10:56:17 +01:00
parent 365f4e0531
commit 491a1e6ffe
3 changed files with 80 additions and 21 deletions

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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()
}