git.iko.soy/parseable-macos
on master help sign in
Replace export dropdown menu with a single button (#24)

The export Menu with separate JSON/CSV options is now a single button that opens an NSSavePanel supporting both file types. The user picks the format via the native file type picker in the save dialog. https://claude.ai/code/session_01BCuYv7jJT3NDdz2cXeeW3g Co-authored-by: Claude <noreply@anthropic.com>

commitc872be471d6b2d127f9c1d9364e0230b9c0bca31 authoriko <mail@iko.soy> dateSun 15 Feb 2026, 18:59 (8 months ago) parentb3a700a
1 file changed+18−29
ParseableViewer/Views/QueryView.swift+18−29
ParseableViewer/Views/QueryView.swift+18−29
@@ -71,9 +71,8 @@ struct QueryView: View {
7171 .disabled(viewModel.sqlQuery.isEmpty)
7272 .accessibilityLabel("Save query")
7373
74− Menu {
75− Button("Export as JSON") { exportJSON() }
76− Button("Export as CSV") { exportCSV() }
74+ Button {
75+ exportResults()
7776 } label: {
7877 Image(systemName: "square.and.arrow.up")
7978 }
@@ -337,36 +336,26 @@ struct QueryView: View {
337336 }
338337 }
339338
340− private func exportJSON() {
341− let results = viewModel.results
342− saveToFile(type: "json") {
343− let encoder = JSONEncoder()
344− encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
345− do {
346− let data = try encoder.encode(results)
347− return String(data: data, encoding: .utf8) ?? "[]"
348− } catch {
349− return nil
350− }
351− }
352− }
353−
354− private func exportCSV() {
355− let results = viewModel.results
356− let columns = viewModel.visibleColumns
357− saveToFile(type: "csv") {
358− QueryViewModel.buildCSV(records: results, columns: columns)
359− }
360− }
361−
362− private func saveToFile(type: String, generate: @Sendable @escaping () -> String?) {
339+ private func exportResults() {
363340 let panel = NSSavePanel()
364− panel.allowedContentTypes = type == "json" ? [.json] : [.commaSeparatedText]
365− panel.nameFieldStringValue = "export.\(type)"
341+ panel.allowedContentTypes = [.json, .commaSeparatedText]
342+ panel.nameFieldStringValue = "export.json"
366343 panel.begin { result in
367344 guard result == .OK, let url = panel.url else { return }
345+ let isCSV = url.pathExtension.lowercased() == "csv"
346+ let results = viewModel.results
347+ let columns = viewModel.visibleColumns
368348 Task.detached(priority: .userInitiated) {
369− guard let content = generate() else {
349+ let content: String?
350+ if isCSV {
351+ content = QueryViewModel.buildCSV(records: results, columns: columns)
352+ } else {
353+ let encoder = JSONEncoder()
354+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
355+ content = (try? encoder.encode(results))
356+ .flatMap { String(data: $0, encoding: .utf8) }
357+ }
358+ guard let content else {
370359 await MainActor.run {
371360 exportError = "Failed to encode data for export"
372361 showExportError = true