import Foundation struct DraftRecord: Codable, Identifiable { var id: String { item.slug.isEmpty ? item.title : item.slug } var serverURL: String var savedAt: Date var item: ContentItem var markdown: String } final class DraftStore { private let folder: URL init() { let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: NSTemporaryDirectory()) folder = base.appendingPathComponent("TCMS Mac Client", isDirectory: true) try? FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true) } func save(item: ContentItem, markdown: String, serverURL: String) throws { let record = DraftRecord(serverURL: serverURL, savedAt: Date(), item: item, markdown: markdown) let data = try JSONEncoder.tcms.encode(record) try data.write(to: fileURL(for: record.id), options: .atomic) } func remove(id: String) { try? FileManager.default.removeItem(at: fileURL(for: id)) } func loadAll(serverURL: String) -> [DraftRecord] { let urls = (try? FileManager.default.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil)) ?? [] return urls.compactMap { url in guard let data = try? Data(contentsOf: url), let record = try? JSONDecoder.tcms.decode(DraftRecord.self, from: data), record.serverURL == serverURL else { return nil } return record } .sorted { $0.savedAt > $1.savedAt } } private func fileURL(for id: String) -> URL { let safe = id.replacingOccurrences(of: "[^A-Za-z0-9._-]+", with: "-", options: .regularExpression) return folder.appendingPathComponent(safe.isEmpty ? "untitled" : safe).appendingPathExtension("json") } } private extension JSONEncoder { static var tcms: JSONEncoder { let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] encoder.dateEncodingStrategy = .iso8601 return encoder } } private extension JSONDecoder { static var tcms: JSONDecoder { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 return decoder } }