Fix formatting-only edits not rendering in the timeline

Components of a formatted body were identified by their plain characters, so an edit
that only added formatting (e.g. wrapping a word in inline code) kept the same ID.
SwiftUI then reused the existing MessageText, whose @State string ignores new input,
and the old unformatted text kept rendering.

Identify components by the hash of their attributed string so formatting changes
produce a new ID.
This commit is contained in:
Mauro Romito
2026-10-01 13:41:13 +02:00
committed by Mauro
parent 7911df9537
commit b65e8f6a7c
2 changed files with 11 additions and 1 deletions
@@ -40,7 +40,8 @@ nonisolated extension AttributedString {
.plainText
}
components.append(AttributedStringBuilderComponent(id: String(attributedString.characters),
// Hash attributes as well as characters so formatting-only edits change the ID and refresh the view.
components.append(AttributedStringBuilderComponent(id: String(attributedString.hashValue),
attributedString: attributedString,
type: componentType))
}
@@ -257,6 +257,15 @@ struct AttributedStringBuilderTests {
#expect(components[1].attributedString.string == "Hidden content")
}
@Test
func componentIDChangesWithFormatting() throws {
let plain = try #require(attributedStringBuilder.fromHTML("test test test"), "Could not build the attributed string")
let formatted = try #require(attributedStringBuilder.fromHTML("test <code>test</code> test"), "Could not build the attributed string")
#expect(plain.string == formatted.string)
#expect(plain.formattedComponents.map(\.id) != formatted.formattedComponents.map(\.id))
}
@Test
func detailsWithoutSummary() throws {
let htmlString = "<details><p>Content</p></details>"