react-native-enriched-markdown-scaffold 0.7.4-scaffold.9 → 1.0.2-scaffold.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (505) hide show
  1. package/README.md +54 -8
  2. package/ReactNativeEnrichedMarkdown.podspec +82 -73
  3. package/android/build.gradle +188 -1
  4. package/android/consumer-rules.pro +1 -1
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +27 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +7 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +24 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +8 -0
  9. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +27 -0
  10. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +7 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +223 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +183 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +65 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1086 -61
  15. package/android/src/main/baseline-prof.txt +1 -1
  16. package/android/src/main/cpp/jni-adapter.cpp +74 -0
  17. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +351 -13
  18. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +62 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +85 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +160 -1
  21. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +10 -1
  22. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +76 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +126 -10
  24. package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyPressEvent.kt +24 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyRequestEvent.kt +28 -0
  26. package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLayoutEvent.kt +36 -0
  27. package/android/src/main/java/com/swmansion/enriched/markdown/events/SelectionChangeEvent.kt +43 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +54 -4
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +923 -462
  30. package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
  34. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
  36. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +108 -7
  37. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
  39. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  65. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  66. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  68. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  69. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +37 -7
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  77. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  78. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  79. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  80. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +35 -28
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +20 -82
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/InlineChipPainter.kt +140 -0
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkChipSpans.kt +177 -0
  90. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +1 -6
  91. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  93. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  94. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  95. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  96. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  97. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  100. package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +16 -1
  101. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +36 -1
  102. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkVariantEntry.kt +7 -3
  103. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  104. package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +3 -0
  105. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  106. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +84 -7
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  117. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  118. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +52 -0
  119. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  120. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  121. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  122. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/HTMLGenerator.kt +2 -1
  123. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/MarkdownExtractor.kt +6 -0
  124. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  125. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  126. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  127. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  128. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/HeadlessActionMode.kt +88 -0
  129. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt +100 -0
  130. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  131. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +72 -16
  132. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionChangeReporter.kt +232 -0
  133. package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +64 -0
  134. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +414 -0
  135. package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
  136. package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
  137. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +61 -11
  138. package/android/src/main/jni/CMakeLists.txt +53 -0
  139. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
  140. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
  141. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
  142. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
  143. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  144. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
  145. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
  146. package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
  147. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +8 -2
  148. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  149. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +13 -6
  150. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathMeasureHelper.kt +2 -1
  151. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +26 -8
  152. package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
  153. package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
  154. package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
  155. package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
  156. package/cpp/highlight/HighlightGrammars.hpp +28 -0
  157. package/cpp/highlight/code_highlight_podspec.rb +211 -0
  158. package/cpp/highlight/gen-registry.mjs +155 -0
  159. package/cpp/highlight/grammar-versions.json +159 -0
  160. package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
  161. package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
  162. package/cpp/parser/MD4CParser.cpp +173 -61
  163. package/cpp/parser/MD4CParser.hpp +1 -0
  164. package/cpp/parser/MarkdownASTNode.hpp +2 -1
  165. package/cpp/wasm/ASTSerializer.cpp +2 -0
  166. package/cpp/wasm/build.sh +1 -0
  167. package/cpp/wasm/md4c_wasm.cpp +3 -1
  168. package/docs/ACCESSIBILITY.md +14 -2
  169. package/docs/API_REFERENCE.md +405 -12
  170. package/docs/BREAKING_CHANGES.md +129 -0
  171. package/docs/CODE_HIGHLIGHT.md +133 -0
  172. package/docs/COPY_OPTIONS.md +61 -3
  173. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  174. package/docs/IMAGE_CACHING.md +30 -0
  175. package/docs/INPUT.md +110 -11
  176. package/docs/LATEX_MATH.md +87 -45
  177. package/docs/MACOS.md +1 -1
  178. package/docs/MARKDOWN_STREAMING.md +27 -1
  179. package/docs/MENTIONS.md +8 -1
  180. package/docs/NATIVE_ASSETS.md +127 -0
  181. package/docs/RTL.md +1 -1
  182. package/docs/STYLES.md +73 -6
  183. package/docs/TESTING.md +112 -0
  184. package/docs/TEXT.md +18 -6
  185. package/docs/WEB.md +4 -1
  186. package/ios/EnrichedMarkdown.h +7 -0
  187. package/ios/EnrichedMarkdown.mm +583 -76
  188. package/ios/EnrichedMarkdownText.h +7 -0
  189. package/ios/EnrichedMarkdownText.mm +299 -73
  190. package/ios/attachments/ENRMImageAttachment.h +9 -0
  191. package/ios/attachments/ENRMImageAttachment.m +143 -40
  192. package/ios/attachments/ENRMMathInlineAttachment+macOS.m +2 -1
  193. package/ios/attachments/ENRMMathInlineAttachment.h +5 -0
  194. package/ios/attachments/ENRMMathInlineAttachment.m +50 -82
  195. package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
  196. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +223 -0
  197. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +183 -0
  198. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +65 -0
  199. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +1086 -61
  200. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +181 -2
  201. package/ios/input/ENRMAutoLinkDetector.h +2 -1
  202. package/ios/input/ENRMAutoLinkDetector.mm +79 -41
  203. package/ios/input/ENRMBlockEditCoordinator.h +71 -0
  204. package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
  205. package/ios/input/ENRMBlockRange.h +28 -0
  206. package/ios/input/ENRMBlockRange.mm +30 -0
  207. package/ios/input/ENRMBlockStore.h +56 -0
  208. package/ios/input/ENRMBlockStore.mm +275 -0
  209. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  210. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  211. package/ios/input/ENRMDetectorPipeline.h +2 -1
  212. package/ios/input/ENRMEditPipeline.h +76 -0
  213. package/ios/input/ENRMEditPipeline.mm +228 -0
  214. package/ios/input/ENRMEditSession.h +36 -0
  215. package/ios/input/ENRMEditSession.mm +63 -0
  216. package/ios/input/ENRMFormattingStore.h +17 -0
  217. package/ios/input/ENRMFormattingStore.mm +95 -105
  218. package/ios/input/ENRMInputBlockType.h +91 -0
  219. package/ios/input/ENRMInputBlockType.mm +5 -0
  220. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  221. package/ios/input/ENRMInputEventEmitter.h +56 -0
  222. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  223. package/ios/input/ENRMInputFormatter.h +29 -69
  224. package/ios/input/ENRMInputFormatter.mm +216 -124
  225. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  226. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  227. package/ios/input/ENRMInputLayoutManager.h +9 -0
  228. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  229. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  230. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  231. package/ios/input/ENRMInputParser.h +5 -6
  232. package/ios/input/ENRMInputParser.mm +461 -178
  233. package/ios/input/ENRMInputRemend.mm +2 -2
  234. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  235. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  236. package/ios/input/ENRMInputTextView.mm +103 -8
  237. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  238. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  239. package/ios/input/ENRMLinkCoordinator.h +41 -0
  240. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  241. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  242. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  243. package/ios/input/ENRMMathAtom.h +26 -0
  244. package/ios/input/ENRMMathAtom.mm +12 -0
  245. package/ios/input/ENRMMentionCoordinator.h +45 -0
  246. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  247. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  248. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  249. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  250. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  251. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  252. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  253. package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
  254. package/ios/input/InputStylePropsUtils.h +109 -68
  255. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  256. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  257. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  258. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  259. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  260. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  261. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  262. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  263. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  264. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  265. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  266. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  267. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  268. package/ios/internals/MeasurementCache.h +41 -9
  269. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  270. package/ios/math/ENRMMathChipController.h +74 -0
  271. package/ios/math/ENRMMathChipController.mm +476 -0
  272. package/ios/math/ENRMRaTeXBridge.swift +27 -6
  273. package/ios/parser/ENRMMarkdownParser.h +1 -8
  274. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  275. package/ios/parser/MarkdownASTNode.h +2 -1
  276. package/ios/parser/MarkdownParserBridge.mm +4 -0
  277. package/ios/renderer/AttributedRenderer.m +20 -2
  278. package/ios/renderer/BaseRenderer.h +17 -2
  279. package/ios/renderer/BaseRenderer.m +30 -0
  280. package/ios/renderer/BlockquoteRenderer.h +1 -1
  281. package/ios/renderer/BlockquoteRenderer.m +144 -35
  282. package/ios/renderer/CodeBlockRenderer.h +1 -1
  283. package/ios/renderer/CodeBlockRenderer.m +27 -23
  284. package/ios/renderer/CodeRenderer.h +1 -1
  285. package/ios/renderer/CodeRenderer.m +3 -1
  286. package/ios/renderer/ENRMImageRenderer.h +1 -1
  287. package/ios/renderer/ENRMImageRenderer.m +5 -1
  288. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  289. package/ios/renderer/ENRMMathInlineRenderer.m +6 -1
  290. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  291. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  292. package/ios/renderer/EmphasisRenderer.h +1 -1
  293. package/ios/renderer/EmphasisRenderer.m +3 -1
  294. package/ios/renderer/HeadingRenderer.h +1 -1
  295. package/ios/renderer/HeadingRenderer.m +13 -6
  296. package/ios/renderer/HighlightRenderer.h +1 -1
  297. package/ios/renderer/HighlightRenderer.m +3 -1
  298. package/ios/renderer/LinkRenderer.h +1 -1
  299. package/ios/renderer/LinkRenderer.m +50 -16
  300. package/ios/renderer/ListItemRenderer.h +13 -1
  301. package/ios/renderer/ListItemRenderer.m +102 -44
  302. package/ios/renderer/ListRenderer.h +2 -2
  303. package/ios/renderer/ListRenderer.m +63 -25
  304. package/ios/renderer/ParagraphRenderer.h +1 -1
  305. package/ios/renderer/ParagraphRenderer.m +16 -8
  306. package/ios/renderer/RenderContext.h +10 -0
  307. package/ios/renderer/RenderContext.m +44 -0
  308. package/ios/renderer/RendererFactory.m +14 -0
  309. package/ios/renderer/StrikethroughRenderer.h +1 -1
  310. package/ios/renderer/StrikethroughRenderer.m +3 -1
  311. package/ios/renderer/StrongRenderer.h +1 -1
  312. package/ios/renderer/StrongRenderer.m +3 -1
  313. package/ios/renderer/SubscriptRenderer.h +1 -1
  314. package/ios/renderer/SubscriptRenderer.m +3 -1
  315. package/ios/renderer/SuperscriptRenderer.h +1 -1
  316. package/ios/renderer/SuperscriptRenderer.m +3 -1
  317. package/ios/renderer/TextRenderer.h +2 -2
  318. package/ios/renderer/TextRenderer.m +3 -1
  319. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  320. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  321. package/ios/renderer/UnderlineRenderer.h +1 -1
  322. package/ios/renderer/UnderlineRenderer.m +3 -1
  323. package/ios/styles/StyleConfig.h +42 -0
  324. package/ios/styles/StyleConfig.mm +216 -0
  325. package/ios/utils/BlockquoteBorder.h +6 -0
  326. package/ios/utils/BlockquoteBorder.m +249 -52
  327. package/ios/utils/CodeBackground.m +3 -65
  328. package/ios/utils/CodeBlockBackground.m +16 -2
  329. package/ios/utils/ENRMAtomicSize.h +65 -0
  330. package/ios/utils/ENRMBlockMenu.h +66 -0
  331. package/ios/utils/ENRMChipDrawing.h +24 -0
  332. package/ios/utils/ENRMChipDrawing.m +83 -0
  333. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  334. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  335. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  336. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  337. package/ios/utils/ENRMFeatureFlags.h +7 -0
  338. package/ios/utils/ENRMImageDownloader.h +11 -1
  339. package/ios/utils/ENRMImageDownloader.m +60 -16
  340. package/ios/utils/ENRMLinkChip.h +70 -0
  341. package/ios/utils/ENRMLinkChip.m +213 -0
  342. package/ios/utils/ENRMLinkLayout.h +50 -0
  343. package/ios/utils/ENRMLinkLayout.m +45 -0
  344. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  345. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  346. package/ios/utils/ENRMMathFallback.h +26 -0
  347. package/ios/utils/ENRMMathFallback.m +18 -0
  348. package/ios/utils/ENRMSelectionChange.h +120 -0
  349. package/ios/utils/ENRMSelectionChange.m +282 -0
  350. package/ios/utils/ENRMTextViewSetup.h +35 -17
  351. package/ios/utils/EditMenuUtils+macOS.m +4 -1
  352. package/ios/utils/EditMenuUtils.h +35 -6
  353. package/ios/utils/EditMenuUtils.m +188 -11
  354. package/ios/utils/HTMLGenerator.m +2 -1
  355. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  356. package/ios/utils/LastElementUtils.h +1 -0
  357. package/ios/utils/ListMarkerDrawer.m +29 -42
  358. package/ios/utils/MarkdownASTSerializer.m +4 -0
  359. package/ios/utils/MarkdownAccessibilityElementBuilder.m +3 -1
  360. package/ios/utils/MarkdownExtractor.m +2 -1
  361. package/ios/utils/ParagraphStyleUtils.h +5 -0
  362. package/ios/utils/ParagraphStyleUtils.m +77 -17
  363. package/ios/utils/RTFExportUtils.m +2 -0
  364. package/ios/utils/RenderedMarkdownSegment.h +13 -1
  365. package/ios/utils/RenderedMarkdownSegment.m +20 -0
  366. package/ios/utils/SegmentRenderer.m +17 -4
  367. package/ios/utils/StreamingMarkdownFilter.h +11 -1
  368. package/ios/utils/StreamingMarkdownFilter.m +98 -2
  369. package/ios/utils/StylePropsUtils.h +110 -1
  370. package/ios/utils/TaskListTapUtils.m +24 -0
  371. package/ios/utils/TextViewLayoutManager.mm +3 -0
  372. package/ios/views/ENRMCodeBlockContainerView.h +60 -0
  373. package/ios/views/ENRMCodeBlockContainerView.m +569 -0
  374. package/ios/views/ENRMMathContainerView.h +23 -0
  375. package/ios/views/ENRMMathContainerView.m +87 -20
  376. package/ios/views/TableContainerView.h +32 -0
  377. package/ios/views/TableContainerView.m +291 -175
  378. package/jest.d.ts +1 -0
  379. package/jest.js +5 -0
  380. package/lib/module/EnrichedMarkdownNativeComponent.ts +203 -1
  381. package/lib/module/EnrichedMarkdownTextInput.js +84 -5
  382. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  383. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  384. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +204 -1
  385. package/lib/module/headingDefaults.js +34 -0
  386. package/lib/module/headingDefaults.js.map +1 -0
  387. package/lib/module/index.js.map +1 -1
  388. package/lib/module/jest/index.js +205 -0
  389. package/lib/module/jest/index.js.map +1 -0
  390. package/lib/module/native/EnrichedMarkdownText.js +199 -15
  391. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  392. package/lib/module/normalizeMarkdownStyle.js +98 -23
  393. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  394. package/lib/module/normalizeMarkdownStyle.web.js +73 -22
  395. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  396. package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
  397. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  398. package/lib/module/normalizeMenuItem.js +1 -1
  399. package/lib/module/utils/textInputState.js +26 -0
  400. package/lib/module/utils/textInputState.js.map +1 -0
  401. package/lib/module/web/EnrichedMarkdownText.js +15 -8
  402. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  403. package/lib/module/web/parseMarkdown.js +4 -3
  404. package/lib/module/web/parseMarkdown.js.map +1 -1
  405. package/lib/module/web/renderers/InlineRenderers.js +102 -3
  406. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  407. package/lib/module/web/renderers/ListRenderers.js +7 -2
  408. package/lib/module/web/renderers/ListRenderers.js.map +1 -1
  409. package/lib/module/web/renderers/index.js +7 -4
  410. package/lib/module/web/renderers/index.js.map +1 -1
  411. package/lib/module/web/styles.js +101 -6
  412. package/lib/module/web/styles.js.map +1 -1
  413. package/lib/module/web/utils.js +14 -0
  414. package/lib/module/web/utils.js.map +1 -1
  415. package/lib/module/web/wasm/md4c.js +0 -0
  416. package/lib/module/web/wasm/md4c.js.map +1 -1
  417. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +185 -2
  418. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  419. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
  420. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  421. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
  422. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  423. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +185 -2
  424. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  425. package/lib/typescript/src/headingDefaults.d.ts +9 -0
  426. package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
  427. package/lib/typescript/src/index.d.ts +3 -3
  428. package/lib/typescript/src/index.d.ts.map +1 -1
  429. package/lib/typescript/src/index.web.d.ts +2 -2
  430. package/lib/typescript/src/index.web.d.ts.map +1 -1
  431. package/lib/typescript/src/jest/index.d.ts +6 -0
  432. package/lib/typescript/src/jest/index.d.ts.map +1 -0
  433. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +5 -5
  434. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  435. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  436. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  437. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
  438. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  439. package/lib/typescript/src/types/MarkdownStyle.d.ts +122 -5
  440. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  441. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +39 -0
  442. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  443. package/lib/typescript/src/types/MarkdownTextProps.d.ts +207 -10
  444. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  445. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +60 -1
  446. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  447. package/lib/typescript/src/types/events.d.ts +85 -0
  448. package/lib/typescript/src/types/events.d.ts.map +1 -1
  449. package/lib/typescript/src/utils/textInputState.d.ts +24 -0
  450. package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
  451. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  452. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  453. package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
  454. package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
  455. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  456. package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
  457. package/lib/typescript/src/web/renderers/index.d.ts +2 -1
  458. package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
  459. package/lib/typescript/src/web/styles.d.ts +2 -1
  460. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  461. package/lib/typescript/src/web/types.d.ts +10 -2
  462. package/lib/typescript/src/web/types.d.ts.map +1 -1
  463. package/lib/typescript/src/web/utils.d.ts +4 -0
  464. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  465. package/package.json +36 -9
  466. package/postinstall.mjs +131 -0
  467. package/ratex-version.json +21 -0
  468. package/src/EnrichedMarkdownNativeComponent.ts +203 -1
  469. package/src/EnrichedMarkdownTextInput.tsx +159 -8
  470. package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  471. package/src/EnrichedMarkdownTextNativeComponent.ts +204 -1
  472. package/src/headingDefaults.ts +21 -0
  473. package/src/index.tsx +15 -0
  474. package/src/index.web.tsx +6 -1
  475. package/src/jest/index.tsx +236 -0
  476. package/src/native/EnrichedMarkdownText.tsx +260 -11
  477. package/src/normalizeMarkdownStyle.ts +117 -16
  478. package/src/normalizeMarkdownStyle.web.ts +76 -15
  479. package/src/normalizeMarkdownTextInputStyle.ts +110 -35
  480. package/src/normalizeMenuItem.ts +1 -1
  481. package/src/types/MarkdownStyle.ts +125 -5
  482. package/src/types/MarkdownStyleInternal.ts +51 -0
  483. package/src/types/MarkdownTextProps.ts +215 -10
  484. package/src/types/MarkdownTextProps.web.ts +62 -1
  485. package/src/types/events.ts +90 -0
  486. package/src/utils/textInputState.ts +35 -0
  487. package/src/web/EnrichedMarkdownText.tsx +26 -5
  488. package/src/web/parseMarkdown.ts +6 -2
  489. package/src/web/renderers/InlineRenderers.tsx +116 -3
  490. package/src/web/renderers/ListRenderers.tsx +14 -2
  491. package/src/web/renderers/index.tsx +6 -2
  492. package/src/web/styles.ts +114 -5
  493. package/src/web/types.ts +10 -1
  494. package/src/web/utils.ts +9 -0
  495. package/src/web/wasm/md4c.js +0 -0
  496. package/vendor-grammars.mjs +511 -0
  497. package/vendor-ratex.mjs +173 -0
  498. package/app.plugin.js +0 -2
  499. package/plugin/build/withAndroidMath.js +0 -19
  500. package/plugin/build/withIosMath.js +0 -28
  501. package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
  502. package/plugin/src/withAndroidMath.ts +0 -24
  503. package/plugin/src/withIosMath.ts +0 -35
  504. package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
  505. package/plugin/tsconfig.build.json +0 -16
@@ -12,6 +12,12 @@ class CheckboxTouchHelper(
12
12
  ) {
13
13
  var onCheckboxTap: ((taskIndex: Int, checked: Boolean, itemText: String) -> Unit)? = null
14
14
 
15
+ /**
16
+ * When false, checkbox taps are ignored: hit-testing is skipped and no
17
+ * gesture is consumed, so the tap falls through to the text view unchanged.
18
+ */
19
+ var isEnabled: Boolean = true
20
+
15
21
  private var touchDownX = 0f
16
22
  private var touchDownY = 0f
17
23
  private var pendingHit: TaskListHitTestResult? = null
@@ -19,6 +25,7 @@ class CheckboxTouchHelper(
19
25
 
20
26
  /** Returns `true` if the event was consumed by a checkbox gesture. */
21
27
  fun onTouchEvent(event: MotionEvent): Boolean {
28
+ if (!isEnabled) return false
22
29
  when (event.actionMasked) {
23
30
  MotionEvent.ACTION_DOWN -> {
24
31
  val hit = TaskListTapUtils.hitTest(textView, event.x, event.y) ?: return false
@@ -8,6 +8,8 @@ import android.text.style.StrikethroughSpan
8
8
  import android.widget.TextView
9
9
  import com.swmansion.enriched.markdown.renderer.SpanStyleCache
10
10
  import com.swmansion.enriched.markdown.spans.BaseListSpan
11
+ import com.swmansion.enriched.markdown.spans.CodeBlockSpan
12
+ import com.swmansion.enriched.markdown.spans.ListMarkerAnchorSpan
11
13
  import com.swmansion.enriched.markdown.spans.TaskListSpan
12
14
  import com.swmansion.enriched.markdown.styles.StyleConfig
13
15
  import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
@@ -101,51 +103,69 @@ object TaskListTapUtils {
101
103
  }
102
104
 
103
105
  val spannable = SpannableStringBuilder(text)
104
- val taskSpans = spannable.getSpans(0, spannable.length, TaskListSpan::class.java)
105
-
106
- val targetSpan = taskSpans.firstOrNull { it.taskIndex == targetIndex }
107
- if (targetSpan == null) {
106
+ val targetSpans =
107
+ spannable
108
+ .getSpans(0, spannable.length, TaskListSpan::class.java)
109
+ .filter { it.taskIndex == targetIndex }
110
+ if (targetSpans.isEmpty()) {
108
111
  return false
109
112
  }
110
113
 
111
- if (targetSpan.isChecked == newChecked) {
114
+ if (targetSpans.all { it.isChecked == newChecked }) {
112
115
  return true
113
116
  }
114
117
 
115
- val spanStart = spannable.getSpanStart(targetSpan)
116
- val spanEnd = spannable.getSpanEnd(targetSpan)
117
- val itemDepth = targetSpan.depth
118
+ val itemDepth = targetSpans.first().depth
119
+ val spanStart = targetSpans.minOf { spannable.getSpanStart(it) }
120
+ val spanEnd = targetSpans.maxOf { spannable.getSpanEnd(it) }
118
121
 
119
122
  val styleCache = SpanStyleCache(styleConfig)
120
- val newTaskSpan =
123
+
124
+ fun toggledCopy(old: TaskListSpan): TaskListSpan =
121
125
  TaskListSpan(
122
126
  taskStyle = styleConfig.taskListStyle,
123
127
  listStyle = styleConfig.listStyle,
124
- depth = itemDepth,
128
+ depth = old.depth,
125
129
  context = textView.context,
126
130
  styleCache = styleCache,
127
131
  taskIndex = targetIndex,
128
132
  isChecked = newChecked,
133
+ drawsMarker = old.drawsMarker,
129
134
  )
130
135
 
131
- spannable.removeSpan(targetSpan)
132
- spannable.setSpan(newTaskSpan, spanStart, spanEnd, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
133
-
134
- val verifySpans = spannable.getSpans(spanStart, spanEnd, TaskListSpan::class.java)
135
- val verifySpan = verifySpans.firstOrNull { it.taskIndex == targetIndex }
136
- if (verifySpan == null || verifySpan.isChecked != newChecked) {
137
- return false
136
+ // Replace every segment of the item — spans are split around code blocks
137
+ for (old in targetSpans) {
138
+ val start = spannable.getSpanStart(old)
139
+ val end = spannable.getSpanEnd(old)
140
+ spannable.removeSpan(old)
141
+ spannable.setSpan(toggledCopy(old), start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
138
142
  }
139
143
 
144
+ // The marker may be drawn by an anchor span wrapping an unattached copy
145
+ spannable
146
+ .getSpans(0, spannable.length, ListMarkerAnchorSpan::class.java)
147
+ .filter { (it.marker as? TaskListSpan)?.taskIndex == targetIndex }
148
+ .forEach { old ->
149
+ val start = spannable.getSpanStart(old)
150
+ val end = spannable.getSpanEnd(old)
151
+ spannable.removeSpan(old)
152
+ spannable.setSpan(
153
+ ListMarkerAnchorSpan(toggledCopy(old.marker as TaskListSpan)),
154
+ start,
155
+ end,
156
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
157
+ )
158
+ }
159
+
140
160
  val taskStyle = styleConfig.taskListStyle
141
161
  val checkedTextColor = taskStyle.checkedTextColor
142
162
  val strikethrough = taskStyle.checkedStrikethrough
143
163
 
144
164
  val excludedRanges =
145
- spannable
146
- .getSpans(spanStart, spanEnd, BaseListSpan::class.java)
147
- .filter { it.depth > itemDepth }
148
- .map { spannable.getSpanStart(it) to spannable.getSpanEnd(it) }
165
+ (
166
+ spannable.getSpans(spanStart, spanEnd, BaseListSpan::class.java).filter { it.depth > itemDepth } +
167
+ spannable.getSpans(spanStart, spanEnd, CodeBlockSpan::class.java).toList()
168
+ ).map { spannable.getSpanStart(it) to spannable.getSpanEnd(it) }
149
169
  .sortedBy { it.first }
150
170
 
151
171
  applyDecorationsToRanges(
@@ -1,6 +1,7 @@
1
1
  package com.swmansion.enriched.markdown.utils.text.span
2
2
 
3
3
  import android.text.SpannableStringBuilder
4
+ import com.swmansion.enriched.markdown.spans.CodeBlockSpan
4
5
  import com.swmansion.enriched.markdown.spans.ImageSpan
5
6
  import com.swmansion.enriched.markdown.spans.LineHeightSpan
6
7
  import com.swmansion.enriched.markdown.spans.MarginBottomSpan
@@ -11,9 +12,9 @@ fun createLineHeightSpan(lineHeight: Float): AndroidLineHeightSpan = LineHeightS
11
12
 
12
13
  /**
13
14
  * Applies [LineHeightSpan] to [start]..[end] but skips ranges occupied by
14
- * block [ImageSpan]s (and their trailing '\n'). Without this, the fixed line
15
- * height would re-clamp the metrics that [ImageSpan.chooseHeight] expanded,
16
- * causing the image to overflow downward and overlap subsequent content.
15
+ * block [ImageSpan]s (and their trailing '\n') and by [CodeBlockSpan]s —
16
+ * both of these apply their own font-metric adjustments that a fixed
17
+ * line height would override.
17
18
  */
18
19
  fun applyLineHeightSkippingImages(
19
20
  builder: SpannableStringBuilder,
@@ -26,19 +27,25 @@ fun applyLineHeightSkippingImages(
26
27
  .getSpans(start, end, ImageSpan::class.java)
27
28
  .filter { !it.isInline }
28
29
  .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
29
- .sortedBy { it.first }
30
+
31
+ val codeBlockRanges =
32
+ builder
33
+ .getSpans(start, end, CodeBlockSpan::class.java)
34
+ .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
35
+
36
+ val excludedRanges = (blockImageRanges + codeBlockRanges).sortedBy { it.first }
30
37
 
31
38
  var pos = start
32
- for ((imgStart, imgEnd) in blockImageRanges) {
33
- if (pos < imgStart) {
39
+ for ((exStart, exEnd) in excludedRanges) {
40
+ if (pos < exStart) {
34
41
  builder.setSpan(
35
42
  createLineHeightSpan(lineHeight),
36
43
  pos,
37
- imgStart,
44
+ exStart,
38
45
  SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
39
46
  )
40
47
  }
41
- val skipEnd = if (imgEnd < end && builder[imgEnd] == '\n') imgEnd + 1 else imgEnd
48
+ val skipEnd = if (exEnd < end && builder[exEnd] == '\n') exEnd + 1 else exEnd
42
49
  pos = maxOf(pos, skipEnd)
43
50
  }
44
51
  if (pos < end) {
@@ -0,0 +1,88 @@
1
+ package com.swmansion.enriched.markdown.utils.text.view
2
+
3
+ import android.view.ActionMode
4
+ import android.view.Menu
5
+ import android.view.MenuInflater
6
+ import android.view.View
7
+ import android.widget.PopupMenu
8
+
9
+ /**
10
+ * Stands in for the floating text-selection toolbar while `contextMenuHidden`
11
+ * is on. An empty menu only hides the AOSP FloatingToolbar; ROMs that draw
12
+ * their own selection popup (MIUI) still show it. Answering the Editor's
13
+ * startActionMode with this mode means no toolbar window is ever created.
14
+ *
15
+ * The callback is the Editor's TextActionModeCallback wrapping ours, so it
16
+ * still gets the full create/prepare/destroy lifecycle: Editor keeps the
17
+ * selection handles and its transient-state bookkeeping, and finish() clears
18
+ * the selection exactly as dismissing the real toolbar would.
19
+ */
20
+ internal class HeadlessActionMode private constructor(
21
+ private val view: View,
22
+ private val callback: ActionMode.Callback,
23
+ ) : ActionMode() {
24
+ // A real framework menu: Editor populates it with MenuItem calls
25
+ // (setAlphabeticShortcut, setShowAsAction, setIntent) a stub can't take.
26
+ private val menu: Menu = PopupMenu(view.context, view).menu
27
+ private var finished = false
28
+
29
+ override fun setTitle(title: CharSequence?) = Unit
30
+
31
+ override fun setTitle(resId: Int) = Unit
32
+
33
+ override fun setSubtitle(subtitle: CharSequence?) = Unit
34
+
35
+ override fun setSubtitle(resId: Int) = Unit
36
+
37
+ override fun setCustomView(view: View?) = Unit
38
+
39
+ override fun invalidate() {
40
+ if (!finished) callback.onPrepareActionMode(this, menu)
41
+ }
42
+
43
+ override fun invalidateContentRect() = Unit
44
+
45
+ override fun hide(duration: Long) = Unit
46
+
47
+ override fun finish() {
48
+ if (finished) return
49
+ finished = true
50
+ callback.onDestroyActionMode(this)
51
+ }
52
+
53
+ override fun getMenu(): Menu = menu
54
+
55
+ override fun getTitle(): CharSequence? = null
56
+
57
+ override fun getSubtitle(): CharSequence? = null
58
+
59
+ override fun getCustomView(): View? = null
60
+
61
+ override fun getMenuInflater(): MenuInflater = MenuInflater(view.context)
62
+
63
+ companion object {
64
+ /** Mirrors the framework: null when the callback declines to start. */
65
+ fun start(
66
+ view: View,
67
+ callback: ActionMode.Callback,
68
+ ): ActionMode? {
69
+ val mode = HeadlessActionMode(view, callback)
70
+ mode.type = TYPE_FLOATING
71
+ if (!callback.onCreateActionMode(mode, mode.menu)) return null
72
+ callback.onPrepareActionMode(mode, mode.menu)
73
+ return mode
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * MIUI 12 (Android 10) replaces android.widget.Editor: text selection never reaches
80
+ * startActionMode or the custom ActionMode callback, so HeadlessActionMode is never used.
81
+ * Its own popup is shown by Editor$HandleView.showActionPopupWindow, which posts a runnable
82
+ * on the TextView (Editor$HandleView$2). Dropping that runnable is the only hook left while
83
+ * `contextMenuHidden` is on.
84
+ */
85
+ private const val ROM_SELECTION_POPUP_SHOWER_PREFIX = "android.widget.Editor\$HandleView"
86
+
87
+ internal fun isRomSelectionPopupShower(action: Runnable?): Boolean =
88
+ action != null && action.javaClass.name.startsWith(ROM_SELECTION_POPUP_SHOWER_PREFIX)
@@ -0,0 +1,100 @@
1
+ package com.swmansion.enriched.markdown.utils.text.view
2
+
3
+ import android.text.Layout
4
+ import android.text.Spanned
5
+ import android.widget.TextView
6
+ import com.facebook.react.uimanager.PixelUtil
7
+ import com.swmansion.enriched.markdown.spans.LinkChipBackgroundSpan
8
+ import com.swmansion.enriched.markdown.spans.LinkSpacers
9
+ import com.swmansion.enriched.markdown.spans.LinkSpan
10
+ import kotlin.math.max
11
+ import kotlin.math.min
12
+
13
+ /** One laid-out line of a link, in dp relative to the reporting view. Covers the drawn chip. */
14
+ data class LinkLayoutFragment(
15
+ val linkIndex: Int,
16
+ val url: String,
17
+ val text: String,
18
+ val x: Float,
19
+ val y: Float,
20
+ val width: Float,
21
+ val height: Float,
22
+ )
23
+
24
+ object LinkLayoutCollector {
25
+ /**
26
+ * Collects the chip rect of every link fragment laid out in [textView], for `onLinkLayout`.
27
+ * Rects are offset by the text view's own padding plus [offsetXPx]/[offsetYPx] (a segment's
28
+ * position inside its container), then converted to dp. Links are numbered in document order
29
+ * from [indexBase]; returns the fragments and the number of links found.
30
+ */
31
+ fun collect(
32
+ textView: TextView,
33
+ offsetXPx: Int = 0,
34
+ offsetYPx: Int = 0,
35
+ indexBase: Int = 0,
36
+ ): Pair<List<LinkLayoutFragment>, Int> {
37
+ val layout = textView.layout ?: return emptyList<LinkLayoutFragment>() to 0
38
+ val text = textView.text as? Spanned ?: return emptyList<LinkLayoutFragment>() to 0
39
+
40
+ // LinkRenderer sets one LinkChipBackgroundSpan per link, spacers included.
41
+ val chips =
42
+ text
43
+ .getSpans(0, text.length, LinkChipBackgroundSpan::class.java)
44
+ .sortedBy { text.getSpanStart(it) }
45
+ val originX = offsetXPx + textView.totalPaddingLeft
46
+ val originY = offsetYPx + textView.totalPaddingTop
47
+
48
+ val fragments = mutableListOf<LinkLayoutFragment>()
49
+ chips.forEachIndexed { i, chipSpan ->
50
+ val start = text.getSpanStart(chipSpan)
51
+ val end = text.getSpanEnd(chipSpan)
52
+ if (start !in 0 until end) return@forEachIndexed
53
+
54
+ val url = text.getSpans(start, end, LinkSpan::class.java).firstOrNull()?.url ?: ""
55
+ val linkText = LinkSpacers.strip(text.subSequence(start, end)).toString()
56
+ val (ascent, descent) = LinkChipBackgroundSpan.linkFontMetrics(text, start, end, textView.paint)
57
+ val paddingVertical = chipSpan.chip.paddingVertical
58
+
59
+ for (line in layout.getLineForOffset(start)..layout.getLineForOffset(end - 1)) {
60
+ val fragmentStart = max(start, layout.getLineStart(line))
61
+ val fragmentEnd =
62
+ LinkChipBackgroundSpan.trimTrailingWhitespace(text, fragmentStart, min(end, layout.getLineEnd(line)))
63
+ if (fragmentEnd <= fragmentStart) continue
64
+
65
+ val left = layout.getPrimaryHorizontal(fragmentStart)
66
+ val right = lineEndHorizontal(layout, line, fragmentEnd)
67
+ val baseline = layout.getLineBaseline(line).toFloat()
68
+ val top = baseline + ascent - paddingVertical
69
+ val bottom = baseline + descent + paddingVertical
70
+
71
+ fragments +=
72
+ LinkLayoutFragment(
73
+ linkIndex = indexBase + i,
74
+ url = url,
75
+ text = linkText,
76
+ x = PixelUtil.toDIPFromPixel(min(left, right) + originX),
77
+ y = PixelUtil.toDIPFromPixel(top + originY),
78
+ width = PixelUtil.toDIPFromPixel(kotlin.math.abs(right - left)),
79
+ height = PixelUtil.toDIPFromPixel(bottom - top),
80
+ )
81
+ }
82
+ }
83
+ return fragments to chips.size
84
+ }
85
+
86
+ /**
87
+ * x of [offset] on [line]. At a line's end offset `getPrimaryHorizontal` reports the start
88
+ * of the next line (the same offset), so a fragment cut mid-word uses the line's right edge.
89
+ */
90
+ private fun lineEndHorizontal(
91
+ layout: Layout,
92
+ line: Int,
93
+ offset: Int,
94
+ ): Float =
95
+ if (offset >= layout.getLineEnd(line) && line < layout.lineCount - 1) {
96
+ layout.getLineRight(line)
97
+ } else {
98
+ layout.getPrimaryHorizontal(offset)
99
+ }
100
+ }
@@ -2,9 +2,8 @@ package com.swmansion.enriched.markdown.utils.text.view
2
2
 
3
3
  import android.os.Handler
4
4
  import android.os.Looper
5
- import android.text.Selection
6
5
  import android.text.Spannable
7
- import android.text.method.LinkMovementMethod
6
+ import android.text.method.ArrowKeyMovementMethod
8
7
  import android.view.MotionEvent
9
8
  import android.view.ViewConfiguration
10
9
  import android.widget.TextView
@@ -13,15 +12,26 @@ import com.swmansion.enriched.markdown.spans.SpoilerSpan
13
12
  import com.swmansion.enriched.markdown.spoiler.SpoilerCapable
14
13
  import kotlin.math.abs
15
14
 
16
- class LinkLongPressMovementMethod : LinkMovementMethod() {
15
+ /**
16
+ * Movement method that adds link tap / long-press and spoiler tap handling on
17
+ * top of [ArrowKeyMovementMethod], the method [setTextIsSelectable] installs.
18
+ *
19
+ * Must never mutate the buffer's Selection spans — the platform Editor
20
+ * manages them during long-press gestures, and removing or overwriting
21
+ * them mid-gesture crashes on some OEM skins.
22
+ * See: https://github.com/software-mansion/enriched-markdown/issues/580
23
+ */
24
+ class LinkLongPressMovementMethod : ArrowKeyMovementMethod() {
17
25
  private val handler = Handler(Looper.getMainLooper())
18
26
  private var longPressRunnable: Runnable? = null
19
27
 
20
28
  private var startX = 0f
21
29
  private var startY = 0f
30
+ private var pressedLink: LinkSpan? = null
22
31
 
23
32
  var isLinkTouchActive: Boolean = false
24
33
  private set
34
+ private var isTouchWithinTextBounds: Boolean = true
25
35
 
26
36
  override fun onTouchEvent(
27
37
  widget: TextView,
@@ -33,9 +43,10 @@ class LinkLongPressMovementMethod : LinkMovementMethod() {
33
43
  startX = event.x
34
44
  startY = event.y
35
45
 
36
- val span = findLinkSpan(widget, buffer, event)
37
- isLinkTouchActive = span != null
38
- span?.let { scheduleLongPress(widget, it) }
46
+ pressedLink = findLinkSpan(widget, buffer, event)
47
+ isLinkTouchActive = pressedLink != null
48
+ isTouchWithinTextBounds = charOffsetAt(widget, event) != null
49
+ pressedLink?.let { scheduleLongPress(widget, it) }
39
50
  }
40
51
 
41
52
  MotionEvent.ACTION_MOVE -> {
@@ -45,17 +56,25 @@ class LinkLongPressMovementMethod : LinkMovementMethod() {
45
56
  ) {
46
57
  cancelLongPress()
47
58
  isLinkTouchActive = false
59
+ pressedLink = null
48
60
  }
49
61
  }
50
62
 
51
63
  MotionEvent.ACTION_UP -> {
52
64
  cancelLongPress()
65
+ val tappedLink = pressedLink
53
66
  isLinkTouchActive = false
54
- if (widget.hasSelection()) {
55
- Selection.removeSelection(buffer)
56
- }
67
+ pressedLink = null
68
+
57
69
  if (handleSpoilerTap(widget, buffer, event)) {
58
- Selection.removeSelection(buffer)
70
+ return true
71
+ }
72
+
73
+ // LinkSpan.onClick itself swallows the click that follows a completed
74
+ // long-press (and resets its internal flag), so it is always invoked
75
+ // for a tap that started and ended on the same link.
76
+ if (tappedLink != null && findLinkSpan(widget, buffer, event) === tappedLink) {
77
+ tappedLink.onClick(widget)
59
78
  return true
60
79
  }
61
80
  }
@@ -63,22 +82,19 @@ class LinkLongPressMovementMethod : LinkMovementMethod() {
63
82
  MotionEvent.ACTION_CANCEL -> {
64
83
  cancelLongPress()
65
84
  isLinkTouchActive = false
66
- if (widget.hasSelection()) {
67
- Selection.removeSelection(buffer)
68
- }
85
+ pressedLink = null
69
86
  }
70
87
  }
71
88
 
72
- val result = super.onTouchEvent(widget, buffer, event)
73
-
74
- // LinkMovementMethod sets a Selection highlight around the link on ACTION_DOWN,
75
- // which causes a visible selection color on the link text while pressed.
76
- // We remove that selection immediately so the user never sees it.
77
- if (event.action == MotionEvent.ACTION_DOWN) {
78
- Selection.removeSelection(buffer)
89
+ // getOffsetForHorizontal snaps to the nearest character, so without this
90
+ // guard taps in empty space past the end of a line would still be treated
91
+ // as text touches. Let them fall through to the parent (e.g. RNGH
92
+ // Pressable) instead.
93
+ if (!isTouchWithinTextBounds) {
94
+ return false
79
95
  }
80
96
 
81
- return result
97
+ return super.onTouchEvent(widget, buffer, event)
82
98
  }
83
99
 
84
100
  private fun scheduleLongPress(
@@ -89,9 +105,6 @@ class LinkLongPressMovementMethod : LinkMovementMethod() {
89
105
 
90
106
  longPressRunnable =
91
107
  Runnable {
92
- if (widget.hasSelection()) {
93
- Selection.removeSelection(widget.text as Spannable)
94
- }
95
108
  // Execute the long click logic on the span
96
109
  if (span.onLongClick(widget)) {
97
110
  // If consumed, cancel the system's own long-press logic (like context menus)
@@ -112,10 +125,28 @@ class LinkLongPressMovementMethod : LinkMovementMethod() {
112
125
  widget: TextView,
113
126
  event: MotionEvent,
114
127
  ): Int? {
115
- val x = event.x.toInt() - widget.totalPaddingLeft + widget.scrollX
116
- val y = event.y.toInt() - widget.totalPaddingTop + widget.scrollY
128
+ val x = event.x - widget.totalPaddingLeft + widget.scrollX
129
+ val y = event.y - widget.totalPaddingTop + widget.scrollY
117
130
  val layout = widget.layout ?: return null
118
- return layout.getOffsetForHorizontal(layout.getLineForVertical(y), x.toFloat())
131
+
132
+ // getLineForVertical clamps to the first/last line for out-of-range
133
+ // values, so taps in vertical padding would silently map to a real
134
+ // line. Reject them before proceeding.
135
+ if (y < 0f || y > layout.height) {
136
+ return null
137
+ }
138
+
139
+ val line = layout.getLineForVertical(y.toInt())
140
+
141
+ // getOffsetForHorizontal snaps to the nearest character even when the
142
+ // tap is outside the actual text content (e.g. empty space after the
143
+ // last word on a line). Guard against that by checking the tap falls
144
+ // within the line's text bounds.
145
+ if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) {
146
+ return null
147
+ }
148
+
149
+ return layout.getOffsetForHorizontal(line, x)
119
150
  }
120
151
 
121
152
  private fun findLinkSpan(