import SwiftUI struct AddBookmarkView: View { @Bindable var viewModel: BookmarksViewModel @Environment(\.dismiss) private var dismiss @State private var url = "" @State private var title = "" @State private var description = "" @State private var tagsText = "" @State private var importText = "" @State private var isSaving = false @State private var error: String? @State private var importMessage: String? var body: some View { NavigationStack { Form { Section { TextEditor(text: $importText) .frame(minHeight: 96) .textInputAutocapitalization(.never) .autocorrectionDisabled() HStack { Button { extractImportText() } label: { Label("Extract Link", systemImage: "link.badge.plus") } .disabled(importText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) Spacer() if let importMessage { Text(importMessage) .font(.caption) .foregroundStyle(.secondary) } } } header: { Text("Import Text") } footer: { Text("Paste an email, newsletter, or message and Marks will pull out the first web link.") } Section { TextField("https://", text: $url) .keyboardType(.URL) .textInputAutocapitalization(.never) .autocorrectionDisabled() } header: { Text("URL") } Section { TextField("Optional", text: $title) } header: { Text("Title") } Section { TextField("Optional", text: $description, axis: .vertical) .lineLimit(2...5) } header: { Text("Notes") } Section { TextField("comma separated", text: $tagsText) .textInputAutocapitalization(.never) .autocorrectionDisabled() } header: { Text("Tags") } footer: { Text("Separate tags with commas") } if let error { Section { Text(error) .foregroundStyle(.red) .font(.footnote) } } } .navigationTitle("Add Bookmark") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } } ToolbarItem(placement: .confirmationAction) { Button("Save") { save() } .disabled(url.trimmingCharacters(in: .whitespaces).isEmpty || isSaving) .overlay { if isSaving { ProgressView().scaleEffect(0.7) } } } } } } private func extractImportText() { guard let payload = IngestPayloadParser.parse(item: importText, typeIdentifier: "public.plain-text") else { importMessage = "No link found" return } url = payload.url if let extractedTitle = payload.title, title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { title = extractedTitle } if description.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { description = payload.notes } importMessage = "Link extracted" error = nil } private func save() { let urlString = url.normalizedURL guard URL(string: urlString) != nil else { error = "Invalid URL" return } let tags = tagsText.parsedTags isSaving = true error = nil Task { do { try await viewModel.addBookmark( url: urlString, title: title.trimmingCharacters(in: .whitespaces), description: description.trimmingCharacters(in: .whitespacesAndNewlines), tags: tags ) dismiss() } catch { self.error = error.localizedDescription isSaving = false } } } }