···1818 @Bindable var store: StoreOf<RepositorySettingsFeature>
19192020 @State private var isSymbolPickerPresented = false
2121- @State private var isImageImporterPresented = false
2221 @State private var isHoveringIconTile = false
23222423 private let previewSize: CGFloat = 40
···5655 onCancel: { isSymbolPickerPresented = false }
5756 )
5857 }
5959- // Accept any image UTType — PNG / JPEG / WebP / HEIC / TIFF / GIF
6060- // / etc. all flow through the same `NSImage(contentsOf:)` render
6161- // path. SVG is listed explicitly because it's a structured-text
6262- // format that doesn't always conform to `.image` in older
6363- // UTType conformance tables. Anything that fails to decode falls
6464- // back to the dashed placeholder at render time.
6565- .fileImporter(
6666- isPresented: $isImageImporterPresented,
6767- allowedContentTypes: [.image, .svg],
6868- allowsMultipleSelection: false
6969- ) { result in
7070- handleImageImportResult(result)
7171- }
7258 }
73597460 // MARK: - Icon row
···11298 isSymbolPickerPresented = true
11399 }
114100 Button("Choose Image…") {
115115- isImageImporterPresented = true
101101+ presentImageImporter()
116102 }
117103 if store.appearance.icon != nil {
118104 Divider()
···338324 }
339325 }
340326341341- private func handleImageImportResult(_ result: Result<[URL], Error>) {
342342- isImageImporterPresented = false
343343- switch result {
344344- case .success(let urls):
345345- guard let url = urls.first else { return }
346346- // `fileImporter` returns security-scoped URLs on macOS — we need
347347- // to start access before reading and stop it on the way out so
348348- // the import store can copy the bytes into the sandboxed app
349349- // support directory.
350350- let needsScope = url.startAccessingSecurityScopedResource()
351351- defer {
352352- if needsScope { url.stopAccessingSecurityScopedResource() }
353353- }
327327+ // Uses `NSOpenPanel` (rather than SwiftUI's `.fileImporter`) so the
328328+ // panel can open at the repo's working directory — most users keep
329329+ // their icon assets next to the project. Accepts any image UTType —
330330+ // PNG / JPEG / WebP / HEIC / TIFF / GIF / etc. all flow through the
331331+ // same `NSImage(contentsOf:)` render path. SVG is listed explicitly
332332+ // because it's a structured-text format that doesn't always conform
333333+ // to `.image` in older UTType conformance tables. Anything that fails
334334+ // to decode falls back to the dashed placeholder at render time.
335335+ private func presentImageImporter() {
336336+ let panel = NSOpenPanel()
337337+ panel.directoryURL = store.rootURL
338338+ panel.allowedContentTypes = [.image, .svg]
339339+ panel.canChooseDirectories = false
340340+ panel.canChooseFiles = true
341341+ panel.allowsMultipleSelection = false
342342+ panel.prompt = "Choose"
343343+ panel.message = "Choose an image to use as this repository's icon."
344344+345345+ panel.begin { response in
346346+ guard response == .OK, let url = panel.url else { return }
354347 store.send(.importUserImage(url))
355355- case .failure(let error):
356356- store.send(.userImageImportFailed(error.localizedDescription))
357348 }
358349 }
359350}