diff --git a/Sources/bskyKit/Models/Timeline.swift b/Sources/bskyKit/Models/Timeline.swift index d11e397..41533cf 100644 --- a/Sources/bskyKit/Models/Timeline.swift +++ b/Sources/bskyKit/Models/Timeline.swift @@ -266,11 +266,65 @@ public struct Record: Codable, Sendable { public let createdAt: Date? public let embed: Embed? public let facets: [Facet]? + public let markdownText: String enum CodingKeys: String, CodingKey { case type = "$type" case text, langs, reply, createdAt, embed, facets } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + type = try container.decodeIfPresent(String.self, forKey: .type) + text = try container.decodeIfPresent(String.self, forKey: .text) + langs = try container.decodeIfPresent([String].self, forKey: .langs) + reply = try container.decodeIfPresent(ReplyDetail.self, forKey: .reply) + createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt) + embed = try container.decodeIfPresent(Embed.self, forKey: .embed) + facets = try container.decodeIfPresent([Facet].self, forKey: .facets) + markdownText = Record.convertToMarkdown(original: text ?? "", facets: facets) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(type, forKey: .type) + try container.encodeIfPresent(text, forKey: .text) + try container.encodeIfPresent(langs, forKey: .langs) + try container.encodeIfPresent(reply, forKey: .reply) + try container.encodeIfPresent(createdAt, forKey: .createdAt) + try container.encodeIfPresent(embed, forKey: .embed) + try container.encodeIfPresent(facets, forKey: .facets) + } + + private static func convertToMarkdown(original: String, facets: [Facet]?) -> String { + guard let facets, !facets.isEmpty else { return original } + + var result = original + let sortedFacets = facets.sorted { $0.index.byteEnd > $1.index.byteEnd } + let utf8 = Array(original.utf8) + + for facet in sortedFacets { + let start = facet.index.byteStart + let end = facet.index.byteEnd + + guard start >= 0, end <= utf8.count, start < end else { continue } + guard let uri = facet.features.first(where: { $0.uri != nil })?.uri else { continue } + + let startBytes = utf8.prefix(start) + let rangeBytes = utf8[start..