diff --git a/ElementX/Sources/Other/Extensions/AttributedString.swift b/ElementX/Sources/Other/Extensions/AttributedString.swift
index d82baa26b..2c935842c 100644
--- a/ElementX/Sources/Other/Extensions/AttributedString.swift
+++ b/ElementX/Sources/Other/Extensions/AttributedString.swift
@@ -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))
}
diff --git a/UnitTests/Sources/AttributedStringBuilderTests.swift b/UnitTests/Sources/AttributedStringBuilderTests.swift
index b33c44cfc..330dd5852 100644
--- a/UnitTests/Sources/AttributedStringBuilderTests.swift
+++ b/UnitTests/Sources/AttributedStringBuilderTests.swift
@@ -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 test 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 = "Content
"