From 11bb2bd0c3be250b1106350542e41d8a34653712 Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Fri, 1 May 2026 13:20:50 +0200 Subject: [PATCH] fix(chat): detach NSOpenPanel image read off MainActor `presentImagePicker()` ran `Data(contentsOf: url)` synchronously on MainActor inside the URL loop before the detached `encode()`. A 24 MP HEIC at 8-15 MB stalled the chat composer per file. The drag/drop and paste paths already read off-main via `loadObject`/`loadDataRepresentation` callbacks; this brings the open-panel branch in line by capturing the URLs into a `Task.detached` and reading bytes there. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Features/Chat/Views/RichChatInputBar.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift b/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift index 7cc32c4..453e0f3 100644 --- a/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift +++ b/scarf/scarf/Features/Chat/Views/RichChatInputBar.swift @@ -486,12 +486,14 @@ struct RichChatInputBar: View { panel.prompt = "Attach" let response = panel.runModal() guard response == .OK else { return } - let urls = panel.urls - let remainingSlots = Self.maxAttachments - attachments.count - for url in urls.prefix(remainingSlots) { - guard let data = try? Data(contentsOf: url) else { continue } - isEncodingAttachment = true - encode(data: data, filename: url.lastPathComponent) + let urls = Array(panel.urls.prefix(Self.maxAttachments - attachments.count)) + guard !urls.isEmpty else { return } + isEncodingAttachment = true + Task.detached(priority: .userInitiated) { + for url in urls { + guard let data = try? Data(contentsOf: url) else { continue } + encode(data: data, filename: url.lastPathComponent) + } } #endif }