diff --git a/.maestro/enrichedInput/flows/dynamic_ol_markers.yaml b/.maestro/enrichedInput/flows/dynamic_ol_markers.yaml
new file mode 100644
index 000000000..6c7eb5b00
--- /dev/null
+++ b/.maestro/enrichedInput/flows/dynamic_ol_markers.yaml
@@ -0,0 +1,56 @@
+appId: swmansion.enriched.example
+---
+# Validates that ordered list margins dynamically adjust when transitioning
+# between single-digit (9) and double-digit (10) indexes.
+- launchApp
+
+- tapOn:
+ id: 'toggle-screen-button'
+
+- runFlow:
+ file: '../subflows/set_editor_value.yaml'
+ env:
+ VALUE: >
+
+
+ - A
+ - B
+ - C
+ - D
+ - E
+ - F
+ - G
+ - H
+ - I
+
+
+
+- tapOn:
+ id: 'size-max-button'
+
+- runFlow:
+ file: '../subflows/capture_or_assert_screenshot.yaml'
+ env:
+ SCREENSHOT_NAME: 'dynamic_ol_markers_before'
+
+- tapOn:
+ id: "editor-input"
+ point: "50%, 95%"
+- pressKey: Enter
+- inputText: "J"
+
+- runFlow:
+ file: '../subflows/capture_or_assert_screenshot.yaml'
+ env:
+ SCREENSHOT_NAME: 'dynamic_ol_markers_after'
+
+- tapOn:
+ id: "editor-input"
+ point: "50%, 95%"
+- pressKey: Backspace
+- pressKey: Backspace
+
+- runFlow:
+ file: '../subflows/capture_or_assert_screenshot.yaml'
+ env:
+ SCREENSHOT_NAME: 'dynamic_ol_markers_revert'
diff --git a/.maestro/enrichedInput/screenshots/android/checkbox_toggle.png b/.maestro/enrichedInput/screenshots/android/checkbox_toggle.png
index b3b661a85..2ebc3fd4b 100644
Binary files a/.maestro/enrichedInput/screenshots/android/checkbox_toggle.png and b/.maestro/enrichedInput/screenshots/android/checkbox_toggle.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_after.png b/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_after.png
new file mode 100644
index 000000000..64f598cec
Binary files /dev/null and b/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_after.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_before.png b/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_before.png
new file mode 100644
index 000000000..f7e4937d1
Binary files /dev/null and b/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_before.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_revert.png b/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_revert.png
new file mode 100644
index 000000000..f567d83b6
Binary files /dev/null and b/.maestro/enrichedInput/screenshots/android/dynamic_ol_markers_revert.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/empty_element_parsing.png b/.maestro/enrichedInput/screenshots/android/empty_element_parsing.png
index 7e28ad4ea..a6fd95fb1 100644
Binary files a/.maestro/enrichedInput/screenshots/android/empty_element_parsing.png and b/.maestro/enrichedInput/screenshots/android/empty_element_parsing.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/empty_lists_parsing.png b/.maestro/enrichedInput/screenshots/android/empty_lists_parsing.png
index d777b0b24..4af0f5167 100644
Binary files a/.maestro/enrichedInput/screenshots/android/empty_lists_parsing.png and b/.maestro/enrichedInput/screenshots/android/empty_lists_parsing.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_copy.png b/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_copy.png
index f717ac4fa..1bea7002c 100644
Binary files a/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_copy.png and b/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_copy.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_cut.png b/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_cut.png
index f717ac4fa..1bea7002c 100644
Binary files a/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_cut.png and b/.maestro/enrichedInput/screenshots/android/extending_paragraph_style_on_paste_after_cut.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/font_scaling.png b/.maestro/enrichedInput/screenshots/android/font_scaling.png
index 6b955aa4e..22495890f 100644
Binary files a/.maestro/enrichedInput/screenshots/android/font_scaling.png and b/.maestro/enrichedInput/screenshots/android/font_scaling.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/html_link_not_extended.png b/.maestro/enrichedInput/screenshots/android/html_link_not_extended.png
index d0198a17d..929c98c7a 100644
Binary files a/.maestro/enrichedInput/screenshots/android/html_link_not_extended.png and b/.maestro/enrichedInput/screenshots/android/html_link_not_extended.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/image_inside_list_parsing.png b/.maestro/enrichedInput/screenshots/android/image_inside_list_parsing.png
index 8c7af1ca3..b6055e22f 100644
Binary files a/.maestro/enrichedInput/screenshots/android/image_inside_list_parsing.png and b/.maestro/enrichedInput/screenshots/android/image_inside_list_parsing.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/inline_styles.png b/.maestro/enrichedInput/screenshots/android/inline_styles.png
index 818443706..c7255dc7d 100644
Binary files a/.maestro/enrichedInput/screenshots/android/inline_styles.png and b/.maestro/enrichedInput/screenshots/android/inline_styles.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/inline_styles_merge.png b/.maestro/enrichedInput/screenshots/android/inline_styles_merge.png
index e1b1cece0..8fa74ed41 100644
Binary files a/.maestro/enrichedInput/screenshots/android/inline_styles_merge.png and b/.maestro/enrichedInput/screenshots/android/inline_styles_merge.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/inline_styles_removal.png b/.maestro/enrichedInput/screenshots/android/inline_styles_removal.png
index 731ac2b2a..c9e51d0c7 100644
Binary files a/.maestro/enrichedInput/screenshots/android/inline_styles_removal.png and b/.maestro/enrichedInput/screenshots/android/inline_styles_removal.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/link_not_extended.png b/.maestro/enrichedInput/screenshots/android/link_not_extended.png
index 8fba4d554..63ac35167 100644
Binary files a/.maestro/enrichedInput/screenshots/android/link_not_extended.png and b/.maestro/enrichedInput/screenshots/android/link_not_extended.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/links_visual_auto.png b/.maestro/enrichedInput/screenshots/android/links_visual_auto.png
index 369f3a2ea..5c4778e6c 100644
Binary files a/.maestro/enrichedInput/screenshots/android/links_visual_auto.png and b/.maestro/enrichedInput/screenshots/android/links_visual_auto.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/links_visual_manual.png b/.maestro/enrichedInput/screenshots/android/links_visual_manual.png
index cc265a9db..65f205f9a 100644
Binary files a/.maestro/enrichedInput/screenshots/android/links_visual_manual.png and b/.maestro/enrichedInput/screenshots/android/links_visual_manual.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/list_newline_insertion.png b/.maestro/enrichedInput/screenshots/android/list_newline_insertion.png
index 12b3b0384..644dc7cce 100644
Binary files a/.maestro/enrichedInput/screenshots/android/list_newline_insertion.png and b/.maestro/enrichedInput/screenshots/android/list_newline_insertion.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_1.png b/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_1.png
index 74947bcfd..8fa6291ad 100644
Binary files a/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_1.png and b/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_1.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_2.png b/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_2.png
index 7a053e6c5..c092fb52d 100644
Binary files a/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_2.png and b/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_2.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_3.png b/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_3.png
index d80b9a756..cb34c00b1 100644
Binary files a/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_3.png and b/.maestro/enrichedInput/screenshots/android/mention_popup_closing_on_cursor_travel_3.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_deleting_second_line.png b/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_deleting_second_line.png
index 5d00fb1ce..a9bd4b766 100644
Binary files a/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_deleting_second_line.png and b/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_deleting_second_line.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_emptying_second_line.png b/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_emptying_second_line.png
index 53049a519..1b4605ea4 100644
Binary files a/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_emptying_second_line.png and b/.maestro/enrichedInput/screenshots/android/ordered_list_renumbering_after_emptying_second_line.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/paragraph_styles_alignment.png b/.maestro/enrichedInput/screenshots/android/paragraph_styles_alignment.png
index 31828ca25..7ef5ac4d0 100644
Binary files a/.maestro/enrichedInput/screenshots/android/paragraph_styles_alignment.png and b/.maestro/enrichedInput/screenshots/android/paragraph_styles_alignment.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png b/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png
index 4d075df95..09ed9ee13 100644
Binary files a/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png and b/.maestro/enrichedInput/screenshots/android/paragraph_styles_blocks.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/paragraph_styles_headings.png b/.maestro/enrichedInput/screenshots/android/paragraph_styles_headings.png
index d94481f0a..cd0e16d10 100644
Binary files a/.maestro/enrichedInput/screenshots/android/paragraph_styles_headings.png and b/.maestro/enrichedInput/screenshots/android/paragraph_styles_headings.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/paragraph_styles_lists.png b/.maestro/enrichedInput/screenshots/android/paragraph_styles_lists.png
index 7bb6e38db..d6716f616 100644
Binary files a/.maestro/enrichedInput/screenshots/android/paragraph_styles_lists.png and b/.maestro/enrichedInput/screenshots/android/paragraph_styles_lists.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/paragraph_styles_no_crash.png b/.maestro/enrichedInput/screenshots/android/paragraph_styles_no_crash.png
index 3278d7428..91d8dbbc8 100644
Binary files a/.maestro/enrichedInput/screenshots/android/paragraph_styles_no_crash.png and b/.maestro/enrichedInput/screenshots/android/paragraph_styles_no_crash.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_bottom.png b/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_bottom.png
index 9c2dd9795..fe9bf8db8 100644
Binary files a/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_bottom.png and b/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_bottom.png differ
diff --git a/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_top.png b/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_top.png
index 3b946339f..298171e07 100644
Binary files a/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_top.png and b/.maestro/enrichedInput/screenshots/android/scrolling_paragraph_styles_top.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_after.png b/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_after.png
new file mode 100644
index 000000000..65dcb826d
Binary files /dev/null and b/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_after.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_before.png b/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_before.png
new file mode 100644
index 000000000..d76caeb35
Binary files /dev/null and b/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_before.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_revert.png b/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_revert.png
new file mode 100644
index 000000000..c3461cd4c
Binary files /dev/null and b/.maestro/enrichedInput/screenshots/ios/dynamic_ol_markers_revert.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/empty_element_parsing.png b/.maestro/enrichedInput/screenshots/ios/empty_element_parsing.png
index ea9dd3310..0a72ed95a 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/empty_element_parsing.png and b/.maestro/enrichedInput/screenshots/ios/empty_element_parsing.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/empty_lists_parsing.png b/.maestro/enrichedInput/screenshots/ios/empty_lists_parsing.png
index 015e7d397..0fc4306d6 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/empty_lists_parsing.png and b/.maestro/enrichedInput/screenshots/ios/empty_lists_parsing.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/image_inside_list_parsing.png b/.maestro/enrichedInput/screenshots/ios/image_inside_list_parsing.png
index 599bd6522..5fbddd064 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/image_inside_list_parsing.png and b/.maestro/enrichedInput/screenshots/ios/image_inside_list_parsing.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/list_newline_insertion.png b/.maestro/enrichedInput/screenshots/ios/list_newline_insertion.png
index ea1bdc616..8d10b6ccf 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/list_newline_insertion.png and b/.maestro/enrichedInput/screenshots/ios/list_newline_insertion.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_deleting_second_line.png b/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_deleting_second_line.png
index d72a13061..b9e6f15a6 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_deleting_second_line.png and b/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_deleting_second_line.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_emptying_second_line.png b/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_emptying_second_line.png
index 431d95210..8116c7122 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_emptying_second_line.png and b/.maestro/enrichedInput/screenshots/ios/ordered_list_renumbering_after_emptying_second_line.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/paragraph_styles_alignment.png b/.maestro/enrichedInput/screenshots/ios/paragraph_styles_alignment.png
index 28f837a59..63b13dd07 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/paragraph_styles_alignment.png and b/.maestro/enrichedInput/screenshots/ios/paragraph_styles_alignment.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/paragraph_styles_lists.png b/.maestro/enrichedInput/screenshots/ios/paragraph_styles_lists.png
index 2bee088d8..d0b9a2ed7 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/paragraph_styles_lists.png and b/.maestro/enrichedInput/screenshots/ios/paragraph_styles_lists.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/paragraph_styles_no_crash.png b/.maestro/enrichedInput/screenshots/ios/paragraph_styles_no_crash.png
index c54bd5738..538c2def3 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/paragraph_styles_no_crash.png and b/.maestro/enrichedInput/screenshots/ios/paragraph_styles_no_crash.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_bottom.png b/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_bottom.png
index 96087f395..7b52e6e43 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_bottom.png and b/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_bottom.png differ
diff --git a/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_top.png b/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_top.png
index 241461ace..2fc6a1463 100644
Binary files a/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_top.png and b/.maestro/enrichedInput/screenshots/ios/scrolling_paragraph_styles_top.png differ
diff --git a/.maestro/enrichedText/flows/dynamic_ol_markers.yaml b/.maestro/enrichedText/flows/dynamic_ol_markers.yaml
new file mode 100644
index 000000000..b8bd5d6e7
--- /dev/null
+++ b/.maestro/enrichedText/flows/dynamic_ol_markers.yaml
@@ -0,0 +1,41 @@
+appId: swmansion.enriched.example
+---
+# Validates that ordered list margins dynamically adjust when transitioning
+# between single-digit (9) and double-digit (10) indexes.
+- launchApp
+
+- tapOn:
+ id: 'toggle-screen-button'
+
+- tapOn:
+ id: 'toggle-enriched-text-screen-button'
+
+- runFlow:
+ file: '../subflows/set_enriched_text_value.yaml'
+ env:
+ VALUE: >
+
+
+ - A
+ - B
+ - C
+
+
+
+ - A
+ - B
+ - C
+ - D
+ - E
+ - F
+ - G
+ - H
+ - I
+ - J
+
+
+
+- runFlow:
+ file: '../subflows/capture_or_assert_screenshot.yaml'
+ env:
+ SCREENSHOT_NAME: 'dynamic_ol_markers'
diff --git a/.maestro/enrichedText/screenshots/android/alignment_visual.png b/.maestro/enrichedText/screenshots/android/alignment_visual.png
index c3e03756a..7c75ce27e 100644
Binary files a/.maestro/enrichedText/screenshots/android/alignment_visual.png and b/.maestro/enrichedText/screenshots/android/alignment_visual.png differ
diff --git a/.maestro/enrichedText/screenshots/android/dynamic_ol_markers.png b/.maestro/enrichedText/screenshots/android/dynamic_ol_markers.png
new file mode 100644
index 000000000..d572bec99
Binary files /dev/null and b/.maestro/enrichedText/screenshots/android/dynamic_ol_markers.png differ
diff --git a/.maestro/enrichedText/screenshots/android/empty_list_elements_display.png b/.maestro/enrichedText/screenshots/android/empty_list_elements_display.png
index 62859c08e..cc42cad1d 100644
Binary files a/.maestro/enrichedText/screenshots/android/empty_list_elements_display.png and b/.maestro/enrichedText/screenshots/android/empty_list_elements_display.png differ
diff --git a/.maestro/enrichedText/screenshots/android/font_scaling.png b/.maestro/enrichedText/screenshots/android/font_scaling.png
index 488791012..4382f8163 100644
Binary files a/.maestro/enrichedText/screenshots/android/font_scaling.png and b/.maestro/enrichedText/screenshots/android/font_scaling.png differ
diff --git a/.maestro/enrichedText/screenshots/android/inline_styles_display.png b/.maestro/enrichedText/screenshots/android/inline_styles_display.png
index fe658211f..806725a3f 100644
Binary files a/.maestro/enrichedText/screenshots/android/inline_styles_display.png and b/.maestro/enrichedText/screenshots/android/inline_styles_display.png differ
diff --git a/.maestro/enrichedText/screenshots/android/paragraph_styles_display.png b/.maestro/enrichedText/screenshots/android/paragraph_styles_display.png
index 112148d77..8a9dce562 100644
Binary files a/.maestro/enrichedText/screenshots/android/paragraph_styles_display.png and b/.maestro/enrichedText/screenshots/android/paragraph_styles_display.png differ
diff --git a/.maestro/enrichedText/screenshots/ios/alignment_visual.png b/.maestro/enrichedText/screenshots/ios/alignment_visual.png
index cffcfac4b..b25f5d91e 100644
Binary files a/.maestro/enrichedText/screenshots/ios/alignment_visual.png and b/.maestro/enrichedText/screenshots/ios/alignment_visual.png differ
diff --git a/.maestro/enrichedText/screenshots/ios/dynamic_ol_markers.png b/.maestro/enrichedText/screenshots/ios/dynamic_ol_markers.png
new file mode 100644
index 000000000..1e5763cb4
Binary files /dev/null and b/.maestro/enrichedText/screenshots/ios/dynamic_ol_markers.png differ
diff --git a/.maestro/enrichedText/screenshots/ios/empty_list_elements_display.png b/.maestro/enrichedText/screenshots/ios/empty_list_elements_display.png
index 03dcbd827..b90b8b7ea 100644
Binary files a/.maestro/enrichedText/screenshots/ios/empty_list_elements_display.png and b/.maestro/enrichedText/screenshots/ios/empty_list_elements_display.png differ
diff --git a/.maestro/enrichedText/screenshots/ios/font_scaling.png b/.maestro/enrichedText/screenshots/ios/font_scaling.png
index 95d5c5808..3c7139eda 100644
Binary files a/.maestro/enrichedText/screenshots/ios/font_scaling.png and b/.maestro/enrichedText/screenshots/ios/font_scaling.png differ
diff --git a/.maestro/enrichedText/screenshots/ios/paragraph_styles_display.png b/.maestro/enrichedText/screenshots/ios/paragraph_styles_display.png
index 8abd1b2af..a09e6d38c 100644
Binary files a/.maestro/enrichedText/screenshots/ios/paragraph_styles_display.png and b/.maestro/enrichedText/screenshots/ios/paragraph_styles_display.png differ
diff --git a/android/src/main/java/com/swmansion/enriched/common/OrderedListMargins.kt b/android/src/main/java/com/swmansion/enriched/common/OrderedListMargins.kt
new file mode 100644
index 000000000..8b365a5d4
--- /dev/null
+++ b/android/src/main/java/com/swmansion/enriched/common/OrderedListMargins.kt
@@ -0,0 +1,73 @@
+package com.swmansion.enriched.common
+
+import android.graphics.Paint
+import android.text.Spannable
+import android.text.style.ParagraphStyle
+import com.swmansion.enriched.common.spans.EnrichedOrderedListSpan
+import com.swmansion.enriched.textinput.utils.getSafeSpanBoundaries
+
+// Recomputes the shared marker column width for every ordered-list item.
+fun updateOrderedListColumnMargins(
+ text: Spannable,
+ paint: Paint,
+) {
+ val spans = text.getSpans(0, text.length, EnrichedOrderedListSpan::class.java)
+ val sortedSpans = spans.sortedBy { text.getSpanStart(it) }
+
+ val changedLists = mutableSetOf>()
+ var currentList = mutableListOf()
+ var currentListChanged = false
+ var previousIndex = 0
+ var highestIndex = 0
+
+ for (span in sortedSpans.reversed()) {
+ if (span.index >= previousIndex) {
+ if (currentListChanged) {
+ // we'll re-layout that list
+ changedLists.add(currentList)
+ }
+
+ // we entered a new distinct list
+ currentList = mutableListOf()
+ currentListChanged = false
+ highestIndex = span.index
+ }
+
+ currentList.add(span)
+ if (span.updateColumnMargin(paint, highestIndex)) {
+ currentListChanged = true
+ }
+
+ previousIndex = span.index
+ }
+
+ if (currentListChanged) {
+ changedLists.add(currentList)
+ }
+
+ // A single empty ParagraphStyle over the whole list forces one re-layout of it.
+ // Uses the same trick as EnrichedSpanWatcher.updateNextLineLayout.
+ for (list in changedLists) {
+ forceOrderedListRelayout(text, list)
+ }
+}
+
+private fun forceOrderedListRelayout(
+ text: Spannable,
+ listSpans: List,
+) {
+ if (listSpans.isEmpty()) return
+
+ // Because the list was populated during a reversed() loop, the elements
+ // are in descending order. last() is the start of the list, first() is the end.
+ val start = text.getSpanStart(listSpans.last())
+ val end = text.getSpanEnd(listSpans.first())
+
+ if (start < 0 || end < 0 || start > end) return
+
+ class EmptySpan : ParagraphStyle
+
+ val (safeStart, safeEnd) = text.getSafeSpanBoundaries(start, end)
+ text.getSpans(safeStart, safeEnd, EmptySpan::class.java).forEach { text.removeSpan(it) }
+ text.setSpan(EmptySpan(), safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+}
diff --git a/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt b/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt
index d57619c91..500766dd8 100644
--- a/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt
+++ b/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt
@@ -10,6 +10,8 @@ import android.text.style.LeadingMarginSpan
import android.text.style.MetricAffectingSpan
import com.swmansion.enriched.common.EnrichedStyle
import com.swmansion.enriched.common.spans.interfaces.EnrichedParagraphSpan
+import kotlin.math.ceil
+import kotlin.math.max
open class EnrichedOrderedListSpan(
var index: Int,
@@ -17,6 +19,27 @@ open class EnrichedOrderedListSpan(
) : MetricAffectingSpan(),
LeadingMarginSpan,
EnrichedParagraphSpan {
+ var columnMargin = enrichedStyle.olMarginLeft
+
+ // Computes the marker column width from the widest marker in the list (".").
+ // Returns true when the column width actually changed, so callers can force a relayout.
+ fun updateColumnMargin(
+ paint: Paint,
+ highestIndex: Int,
+ ): Boolean {
+ val highestIndexText = "$highestIndex."
+
+ val originalTypeface = paint.typeface
+ paint.typeface = getTypeface(enrichedStyle.olMarkerFontWeight, originalTypeface)
+ val highestIndexWidth = ceil(paint.measureText(highestIndexText)).toInt()
+ paint.typeface = originalTypeface
+
+ val newColumnMargin = max(enrichedStyle.olMarginLeft, highestIndexWidth)
+ if (newColumnMargin == columnMargin) return false
+ columnMargin = newColumnMargin
+ return true
+ }
+
override fun updateMeasureState(p0: TextPaint) {
// Do nothing, but inform layout that this span affects text metrics
}
@@ -25,7 +48,7 @@ open class EnrichedOrderedListSpan(
// Do nothing, but inform layout that this span affects text metrics
}
- override fun getLeadingMargin(first: Boolean): Int = enrichedStyle.olMarginLeft + enrichedStyle.olGapWidth
+ override fun getLeadingMargin(first: Boolean): Int = columnMargin + enrichedStyle.olGapWidth
override fun drawLeadingMargin(
canvas: Canvas,
@@ -42,17 +65,17 @@ open class EnrichedOrderedListSpan(
layout: Layout?,
) {
if (first) {
+ val originalColor = paint.color
+ val originalTypeface = paint.typeface
+ paint.color = enrichedStyle.olMarkerColor ?: originalColor
+ paint.typeface = getTypeface(enrichedStyle.olMarkerFontWeight, originalTypeface)
+
val text = "$index."
val width = paint.measureText(text)
val yPosition = baseline.toFloat()
- val xPosition = (enrichedStyle.olMarginLeft + x - width / 2) * dir
-
- val originalColor = paint.color
- val originalTypeface = paint.typeface
+ val xPosition = (columnMargin + x - width) * dir
- paint.color = enrichedStyle.olMarkerColor ?: originalColor
- paint.typeface = getTypeface(enrichedStyle.olMarkerFontWeight, originalTypeface)
canvas.drawText(text, xPosition, yPosition, paint)
paint.color = originalColor
diff --git a/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt b/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt
index d1ee9b4ff..c79890bee 100644
--- a/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt
+++ b/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt
@@ -98,7 +98,7 @@ data class EnrichedTextStyle(
blockquoteStripeWidth = parseFloat(blockquote, "borderWidth", allowFontScaling).toInt(),
blockquoteGapWidth = parseFloat(blockquote, "gapWidth", allowFontScaling).toInt(),
olGapWidth = parseFloat(orderedList, "gapWidth", allowFontScaling).toInt(),
- olMarginLeft = calculateOlMarginLeft(fontSize, parseFloat(orderedList, "marginLeft", allowFontScaling).toInt()),
+ olMarginLeft = parseFloat(orderedList, "marginLeft", allowFontScaling).toInt(),
olMarkerFontWeight = parseOptionalFontWeight(orderedList, "markerFontWeight"),
olMarkerColor = parseOptionalColor(context, orderedList, "markerColor"),
ulGapWidth = parseFloat(unorderedList, "gapWidth", allowFontScaling).toInt(),
@@ -169,14 +169,6 @@ data class EnrichedTextStyle(
return parseFontWeight(weight)
}
- private fun calculateOlMarginLeft(
- fontSize: Int,
- userMargin: Int,
- ): Int {
- val leadMargin = fontSize / 2
- return leadMargin + userMargin
- }
-
private fun parseMentionsStyle(
context: ReactContext,
map: ReadableMap?,
diff --git a/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt b/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt
index 53a0a503a..78f89e660 100644
--- a/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt
+++ b/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt
@@ -27,6 +27,7 @@ import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.common.GumboNormalizer
import com.swmansion.enriched.common.parser.EnrichedParser
import com.swmansion.enriched.common.pixelFromSpOrDp
+import com.swmansion.enriched.common.updateOrderedListColumnMargins
import com.swmansion.enriched.text.spans.EnrichedTextImageSpan
import com.swmansion.enriched.text.spans.interfaces.EnrichedTextClickableSpan
import com.swmansion.enriched.text.spans.interfaces.EnrichedTextSpan
@@ -179,6 +180,7 @@ class EnrichedTextView : AppCompatTextView {
val parsed = parseText(text, style)
if (parsed != null) {
parsedText = parsed
+ (parsed as? Spannable)?.let { updateOrderedListColumnMargins(it, paint) }
setText(parsed, BufferType.NORMAL)
observeAsyncImages()
} else {
@@ -283,6 +285,7 @@ class EnrichedTextView : AppCompatTextView {
}
if (modified) {
+ updateOrderedListColumnMargins(spannable, paint)
this.text = spannable
}
}
diff --git a/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt b/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt
index 023cc20fc..88beac004 100644
--- a/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt
+++ b/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt
@@ -43,6 +43,7 @@ import com.swmansion.enriched.common.EnrichedSpanFlags
import com.swmansion.enriched.common.GumboNormalizer
import com.swmansion.enriched.common.parser.EnrichedParser
import com.swmansion.enriched.common.pixelFromSpOrDp
+import com.swmansion.enriched.common.updateOrderedListColumnMargins
import com.swmansion.enriched.textinput.events.MentionHandler
import com.swmansion.enriched.textinput.events.OnContextMenuItemPressEvent
import com.swmansion.enriched.textinput.events.OnInputBlurEvent
@@ -448,6 +449,9 @@ class EnrichedTextInputView :
runAsATransaction {
val newText = if (shouldParseHtml) parseText(value) else value
+ // Compute ordered-list marker column widths before setText so the first layout reserves
+ // the correct leading margin (getLeadingMargin runs during layout, before drawing).
+ (newText as? Spannable)?.let { updateOrderedListColumnMargins(it, paint) }
setText(newText)
applyLineSpacing()
diff --git a/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt b/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt
index 5a772ded9..a02e8398f 100644
--- a/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt
+++ b/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt
@@ -113,9 +113,7 @@ class HtmlStyle : EnrichedStyle {
blockquoteStripeWidth = parseFloat(blockquoteStyle, "borderWidth").toInt()
val olStyle = style.getMap("ol")
- val userDefinedMarginLeft = parseFloat(olStyle, "marginLeft").toInt()
- val calculatedMarginLeft = calculateOlMarginLeft(view, userDefinedMarginLeft)
- olMarginLeft = calculatedMarginLeft
+ olMarginLeft = parseFloat(olStyle, "marginLeft").toInt()
olGapWidth = parseFloat(olStyle, "gapWidth").toInt()
olMarkerColor = parseOptionalColor(olStyle, "markerColor")
olMarkerFontWeight = parseOptionalFontWeight(olStyle, "markerFontWeight")
@@ -213,16 +211,6 @@ class HtmlStyle : EnrichedStyle {
throw Error("Specified textDecorationLine value is not supported: $underline. Supported values are 'underline' and 'none'.")
}
- private fun calculateOlMarginLeft(
- view: EnrichedTextInputView?,
- userMargin: Int,
- ): Int {
- val fontSize = view?.fontSize?.toInt() ?: 0
- val leadMargin = fontSize / 2
-
- return leadMargin + userMargin
- }
-
private fun ensureValueIsSet(
map: ReadableMap?,
key: String,
diff --git a/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt b/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt
index 236560f10..3ab7c410b 100644
--- a/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt
+++ b/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt
@@ -6,6 +6,7 @@ import android.text.SpannableStringBuilder
import android.text.Spanned
import com.swmansion.enriched.common.EnrichedConstants
import com.swmansion.enriched.common.EnrichedSpanFlags
+import com.swmansion.enriched.common.updateOrderedListColumnMargins
import com.swmansion.enriched.textinput.EnrichedTextInputView
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
import com.swmansion.enriched.textinput.spans.EnrichedInputOrderedListSpan
@@ -115,6 +116,8 @@ class ListStyles(
val index = getOrderedListIndex(text, spanStart)
span.setListIndex(index)
}
+
+ updateOrderedListColumnMargins(text, view.paint)
}
private fun toggleStyle(
diff --git a/apps/example/src/constants/editorConfig.ts b/apps/example/src/constants/editorConfig.ts
index 2b9d44b0b..63043835e 100644
--- a/apps/example/src/constants/editorConfig.ts
+++ b/apps/example/src/constants/editorConfig.ts
@@ -111,7 +111,7 @@ export const htmlStyle = {
},
ol: {
gapWidth: 16,
- marginLeft: 24,
+ marginLeft: 16,
markerColor: 'navy',
markerFontWeight: 'bold',
},
diff --git a/ios/extensions/LayoutManagerExtension.mm b/ios/extensions/LayoutManagerExtension.mm
index 72c9620eb..38d033b05 100644
--- a/ios/extensions/LayoutManagerExtension.mm
+++ b/ios/extensions/LayoutManagerExtension.mm
@@ -448,7 +448,7 @@ - (void)drawDecimal:(id)host
indent:(CGFloat)indent {
CGFloat gapWidth = [host.config orderedListGapWidth];
CGSize markerSize = [marker sizeWithAttributes:markerAttributes];
- CGFloat markerX = origin.x + indent - gapWidth - markerSize.width / 2;
+ CGFloat markerX = origin.x + indent - gapWidth - markerSize.width;
UIFont *markerFont = markerAttributes[NSFontAttributeName];
// drawAtPoint draws from the top-left bounding box of the string.
diff --git a/ios/inputAttributesManager/InputAttributesManager.mm b/ios/inputAttributesManager/InputAttributesManager.mm
index fc07d871f..db2e674b4 100644
--- a/ios/inputAttributesManager/InputAttributesManager.mm
+++ b/ios/inputAttributesManager/InputAttributesManager.mm
@@ -62,6 +62,10 @@ - (void)clearRemovedTypingAttributes {
}
- (void)handleDirtyRangesStyling {
+ // snapshot edited ranges (including 0-length ones) before filtering, so
+ // we can use them when recalculating adjacent ordered lists
+ NSArray *editedRangesForListRecalc = [_dirtyRanges copy];
+
// Filter out 0 length ranges for styling.
NSPredicate *predicate = [NSPredicate
predicateWithBlock:^BOOL(NSValue *evaluatedObject, NSDictionary *_) {
@@ -122,6 +126,16 @@ - (void)handleDirtyRangesStyling {
}
}
}
+ // refresh ordered lists adjacent to any edit
+ OrderedListStyle *orderedListStyle =
+ (OrderedListStyle *)_input->stylesDict[@([OrderedListStyle getType])];
+ if (orderedListStyle != nil) {
+ for (NSValue *rangeObj in editedRangesForListRecalc) {
+ [orderedListStyle
+ recalculateListsAroundEditedRange:[rangeObj rangeValue]];
+ }
+ }
+
// do the typing attributes management, with no selection
[self manageTypingAttributesWithOnlySelection:NO];
diff --git a/ios/interfaces/StyleHeaders.h b/ios/interfaces/StyleHeaders.h
index aca5d4a3c..d59b9c424 100644
--- a/ios/interfaces/StyleHeaders.h
+++ b/ios/interfaces/StyleHeaders.h
@@ -76,6 +76,7 @@
@end
@interface OrderedListStyle : StyleBase
+- (void)recalculateListsAroundEditedRange:(NSRange)range;
@end
@interface CheckboxListStyle : StyleBase
diff --git a/ios/styles/OrderedListStyle.mm b/ios/styles/OrderedListStyle.mm
index 717d0887c..718ac7457 100644
--- a/ios/styles/OrderedListStyle.mm
+++ b/ios/styles/OrderedListStyle.mm
@@ -22,24 +22,108 @@ - (BOOL)needsZWS {
return YES;
}
-- (CGFloat)headIndent {
- return [self.host.config orderedListMarginLeft] +
- [self.host.config orderedListGapWidth];
-}
-
- (void)applyStyling:(NSRange)range {
// lists are drawn manually
- // margin before marker + gap between marker and paragraph
- CGFloat listHeadIndent = [self headIndent];
+
+ // if the widest counter ("N.") width overflows the initially given margin,
+ // we expand that margin. Every item in the same contiguous list must share
+ // the same column width, so we expand to the full ordered list occurrence and
+ // re-indent all of it - even when only a single paragraph is dirty (e.g. an
+ // item was just added)
+ NSInteger itemCount = 0;
+ NSRange listRange = [self contiguousOrderedListRangeContaining:range
+ itemCount:&itemCount];
+
+ [self applyIndentForListRange:listRange itemCount:itemCount];
+}
+
+// re-styling is normally run only on dirty-ranges, but a dirty-range
+// may change an ordered list structure and those lists need to be
+// re-styled. E.g. it happens when we remove an ordered list
+// element - it affects the adjacent lists, as their ordinals are different
+// and the computed margin might be stale
+- (void)recalculateListsAroundEditedRange:(NSRange)range {
+ NSUInteger length = self.host.textView.textStorage.string.length;
+ NSUInteger start = range.location;
+ NSUInteger end = NSMaxRange(range);
+
+ // look for ordered lists in adjacent locations
+ NSMutableArray *seeds = [NSMutableArray array];
+ if (start > 0) {
+ [seeds addObject:@(start - 1)];
+ }
+ [seeds addObject:@(start)];
+ [seeds addObject:@(end)];
+
+ // dedupe so each surviving contiguous list is recomputed at most once
+ NSMutableArray *handled = [NSMutableArray array];
+
+ for (NSNumber *seedNum in seeds) {
+ NSUInteger seed = seedNum.unsignedIntegerValue;
+ if (seed >= length) {
+ continue;
+ }
+ if (![self detect:NSMakeRange(seed, 0)]) {
+ continue;
+ }
+
+ BOOL alreadyHandled = NO;
+ for (NSValue *handledRange in handled) {
+ if (NSLocationInRange(seed, [handledRange rangeValue])) {
+ alreadyHandled = YES;
+ break;
+ }
+ }
+ if (alreadyHandled) {
+ continue;
+ }
+
+ NSInteger itemCount = 0;
+ NSRange listRange =
+ [self contiguousOrderedListRangeContaining:NSMakeRange(seed, 0)
+ itemCount:&itemCount];
+ [handled addObject:[NSValue valueWithRange:listRange]];
+ [self applyIndentForListRange:listRange itemCount:itemCount];
+ }
+}
+
+// computes the shared marker-column indent for a list of the given item
+// count. The largest marker value equals the item count (numbering starts
+// at 1); if its width overflows the configured margin we expand to fit it
+- (CGFloat)headIndentForItemCount:(NSInteger)itemCount {
+ NSString *widestMarker =
+ [NSString stringWithFormat:@"%d.", (int)MAX(itemCount, 1)];
+ CGFloat widestMarkerWidth =
+ [widestMarker sizeWithAttributes:@{
+ NSFontAttributeName : [self.host.config orderedListMarkerFont]
+ }]
+ .width;
+
+ CGFloat markerColumnWidth =
+ MAX([self.host.config orderedListMarginLeft], widestMarkerWidth);
+ return markerColumnWidth + [self.host.config orderedListGapWidth];
+}
+
+- (void)applyIndentForListRange:(NSRange)listRange
+ itemCount:(NSInteger)itemCount {
+ CGFloat listHeadIndent = [self headIndentForItemCount:itemCount];
[self.host.textView.textStorage
enumerateAttribute:NSParagraphStyleAttributeName
- inRange:range
+ inRange:listRange
options:0
usingBlock:^(id _Nullable value, NSRange range,
BOOL *_Nonnull stop) {
+ NSParagraphStyle *existing = (NSParagraphStyle *)value;
+ // skip re-styling paragraphs that don't require it
+ if (existing != nullptr &&
+ existing.headIndent == listHeadIndent &&
+ existing.firstLineHeadIndent == listHeadIndent) {
+ return;
+ }
NSMutableParagraphStyle *pStyle =
- [(NSParagraphStyle *)value mutableCopy];
+ existing ? [existing mutableCopy]
+ : [[NSMutableParagraphStyle alloc] init];
pStyle.headIndent = listHeadIndent;
pStyle.firstLineHeadIndent = listHeadIndent;
[self.host.textView.textStorage
@@ -58,10 +142,86 @@ - (void)applyStylingToTypingAttrs:(NSMutableDictionary *)attributes {
[attributes[NSParagraphStyleAttributeName] mutableCopy];
if (pStyle == nil)
return;
- CGFloat indent = [self headIndent];
- pStyle.headIndent = indent;
- pStyle.firstLineHeadIndent = indent;
+
+ NSUInteger location = self.host.textView.selectedRange.location;
+ NSUInteger length = self.host.textView.textStorage.length;
+
+ if (location < length) {
+ // applying styling to typing attributes always happen after applying
+ // the styles, so we can lookup the existing style for the indent
+ NSParagraphStyle *existingStyle =
+ [self.host.textView.textStorage attribute:NSParagraphStyleAttributeName
+ atIndex:location
+ effectiveRange:NULL];
+
+ if (existingStyle) {
+ pStyle.headIndent = existingStyle.headIndent;
+ pStyle.firstLineHeadIndent = existingStyle.firstLineHeadIndent;
+ }
+ } else {
+ CGFloat fallbackIndent = [self headIndentForItemCount:1];
+ pStyle.headIndent = fallbackIndent;
+ pStyle.firstLineHeadIndent = fallbackIndent;
+ }
+
attributes[NSParagraphStyleAttributeName] = pStyle;
}
+// walks paragraphs backward and forward from the given range to find the full
+// contiguous run of ordered-list items it belongs to, and counts them
+- (NSRange)contiguousOrderedListRangeContaining:(NSRange)range
+ itemCount:(NSInteger *)outCount {
+ NSString *fullText = self.host.textView.textStorage.string;
+ NSUInteger length = fullText.length;
+ if (length == 0) {
+ if (outCount != nullptr) {
+ *outCount = 0;
+ }
+ return NSMakeRange(range.location, 0);
+ }
+
+ NSUInteger seedLocation = MIN(range.location, length - 1);
+ NSRange initialParagraph =
+ [fullText paragraphRangeForRange:NSMakeRange(seedLocation, 0)];
+ NSRange firstParagraph = initialParagraph;
+
+ NSInteger precedingCount = 0;
+
+ // seek backward over preceding ordered-list paragraphs, counting items
+ while (firstParagraph.location > 0) {
+ NSRange previous = [fullText
+ paragraphRangeForRange:NSMakeRange(firstParagraph.location - 1, 0)];
+ if (![self detect:NSMakeRange(previous.location, 0)]) {
+ break;
+ }
+ firstParagraph = previous;
+ precedingCount += 1;
+ }
+
+ // seek forward over following ordered-list paragraphs, counting items
+ NSInteger followingCount = 0;
+ NSRange lastParagraph = initialParagraph;
+ NSRange cursor = initialParagraph;
+ while (true) {
+ lastParagraph = cursor;
+ NSUInteger nextLocation = NSMaxRange(cursor);
+ if (nextLocation >= length) {
+ break;
+ }
+ NSRange next =
+ [fullText paragraphRangeForRange:NSMakeRange(nextLocation, 0)];
+ if (![self detect:NSMakeRange(next.location, 0)]) {
+ break;
+ }
+ cursor = next;
+ followingCount += 1;
+ }
+
+ if (outCount != nullptr) {
+ *outCount = precedingCount + followingCount + 1;
+ }
+ return NSMakeRange(firstParagraph.location,
+ NSMaxRange(lastParagraph) - firstParagraph.location);
+}
+
@end
diff --git a/ios/styles/UnorderedListStyle.mm b/ios/styles/UnorderedListStyle.mm
index 160ac946b..0e94894ac 100644
--- a/ios/styles/UnorderedListStyle.mm
+++ b/ios/styles/UnorderedListStyle.mm
@@ -24,6 +24,7 @@ - (BOOL)needsZWS {
- (CGFloat)headIndent {
return [self.host.config unorderedListMarginLeft] +
+ [self.host.config unorderedListBulletSize] +
[self.host.config unorderedListGapWidth];
}