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

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 (469) hide show
  1. package/README.md +54 -8
  2. package/ReactNativeEnrichedMarkdown.podspec +82 -73
  3. package/android/build.gradle +186 -1
  4. package/android/consumer-rules.pro +1 -1
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +9 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +3 -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 +9 -0
  10. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +3 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +35 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +886 -23
  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 +189 -13
  18. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +17 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +50 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +48 -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 +42 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +82 -4
  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/input/EnrichedMarkdownTextInputManager.kt +54 -4
  26. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +901 -462
  27. package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
  30. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +101 -7
  34. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
  36. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
  39. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  65. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  66. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  68. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  69. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +30 -27
  77. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  78. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  79. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +27 -3
  80. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  90. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  91. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  93. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  94. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  95. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  96. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  97. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  100. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +37 -7
  101. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  102. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  103. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  104. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  105. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  106. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +49 -15
  117. package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +45 -0
  118. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +396 -0
  119. package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
  120. package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
  121. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +36 -9
  122. package/android/src/main/jni/CMakeLists.txt +53 -0
  123. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
  124. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
  125. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
  126. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
  127. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  128. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
  129. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
  130. package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
  131. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +7 -2
  132. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  133. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +6 -4
  134. package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
  135. package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
  136. package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
  137. package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
  138. package/cpp/highlight/HighlightGrammars.hpp +28 -0
  139. package/cpp/highlight/code_highlight_podspec.rb +211 -0
  140. package/cpp/highlight/gen-registry.mjs +155 -0
  141. package/cpp/highlight/grammar-versions.json +159 -0
  142. package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
  143. package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
  144. package/cpp/parser/MD4CParser.cpp +173 -61
  145. package/cpp/parser/MD4CParser.hpp +1 -0
  146. package/cpp/parser/MarkdownASTNode.hpp +2 -1
  147. package/cpp/wasm/ASTSerializer.cpp +2 -0
  148. package/cpp/wasm/build.sh +1 -0
  149. package/cpp/wasm/md4c_wasm.cpp +3 -1
  150. package/docs/ACCESSIBILITY.md +14 -2
  151. package/docs/API_REFERENCE.md +234 -12
  152. package/docs/BREAKING_CHANGES.md +129 -0
  153. package/docs/CODE_HIGHLIGHT.md +133 -0
  154. package/docs/COPY_OPTIONS.md +47 -3
  155. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  156. package/docs/IMAGE_CACHING.md +30 -0
  157. package/docs/INPUT.md +108 -11
  158. package/docs/LATEX_MATH.md +87 -45
  159. package/docs/MACOS.md +1 -1
  160. package/docs/MARKDOWN_STREAMING.md +27 -1
  161. package/docs/NATIVE_ASSETS.md +127 -0
  162. package/docs/RTL.md +1 -1
  163. package/docs/STYLES.md +46 -6
  164. package/docs/TESTING.md +112 -0
  165. package/docs/TEXT.md +16 -6
  166. package/docs/WEB.md +3 -1
  167. package/ios/EnrichedMarkdown.h +7 -0
  168. package/ios/EnrichedMarkdown.mm +284 -75
  169. package/ios/EnrichedMarkdownText.h +7 -0
  170. package/ios/EnrichedMarkdownText.mm +101 -73
  171. package/ios/attachments/ENRMImageAttachment.h +9 -0
  172. package/ios/attachments/ENRMImageAttachment.m +143 -40
  173. package/ios/attachments/ENRMMathInlineAttachment.h +2 -0
  174. package/ios/attachments/ENRMMathInlineAttachment.m +48 -81
  175. package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
  176. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  177. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  178. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +35 -0
  179. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +886 -23
  180. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +131 -0
  181. package/ios/input/ENRMAutoLinkDetector.h +2 -1
  182. package/ios/input/ENRMAutoLinkDetector.mm +79 -41
  183. package/ios/input/ENRMBlockEditCoordinator.h +71 -0
  184. package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
  185. package/ios/input/ENRMBlockRange.h +28 -0
  186. package/ios/input/ENRMBlockRange.mm +30 -0
  187. package/ios/input/ENRMBlockStore.h +56 -0
  188. package/ios/input/ENRMBlockStore.mm +275 -0
  189. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  190. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  191. package/ios/input/ENRMDetectorPipeline.h +2 -1
  192. package/ios/input/ENRMEditPipeline.h +76 -0
  193. package/ios/input/ENRMEditPipeline.mm +228 -0
  194. package/ios/input/ENRMEditSession.h +36 -0
  195. package/ios/input/ENRMEditSession.mm +63 -0
  196. package/ios/input/ENRMFormattingStore.h +17 -0
  197. package/ios/input/ENRMFormattingStore.mm +95 -105
  198. package/ios/input/ENRMInputBlockType.h +91 -0
  199. package/ios/input/ENRMInputBlockType.mm +5 -0
  200. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  201. package/ios/input/ENRMInputEventEmitter.h +56 -0
  202. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  203. package/ios/input/ENRMInputFormatter.h +29 -69
  204. package/ios/input/ENRMInputFormatter.mm +216 -124
  205. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  206. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  207. package/ios/input/ENRMInputLayoutManager.h +9 -0
  208. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  209. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  210. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  211. package/ios/input/ENRMInputParser.h +5 -6
  212. package/ios/input/ENRMInputParser.mm +461 -178
  213. package/ios/input/ENRMInputRemend.mm +2 -2
  214. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  215. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  216. package/ios/input/ENRMInputTextView.mm +103 -8
  217. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  218. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  219. package/ios/input/ENRMLinkCoordinator.h +41 -0
  220. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  221. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  222. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  223. package/ios/input/ENRMMathAtom.h +26 -0
  224. package/ios/input/ENRMMathAtom.mm +12 -0
  225. package/ios/input/ENRMMentionCoordinator.h +45 -0
  226. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  227. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  228. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  229. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  230. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  231. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  232. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  233. package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
  234. package/ios/input/InputStylePropsUtils.h +109 -68
  235. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  236. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  237. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  238. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  239. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  240. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  241. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  242. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  243. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  244. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  245. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  246. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  247. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  248. package/ios/internals/MeasurementCache.h +33 -7
  249. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  250. package/ios/math/ENRMMathChipController.h +74 -0
  251. package/ios/math/ENRMMathChipController.mm +476 -0
  252. package/ios/math/ENRMRaTeXBridge.swift +4 -1
  253. package/ios/parser/ENRMMarkdownParser.h +1 -8
  254. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  255. package/ios/parser/MarkdownASTNode.h +2 -1
  256. package/ios/parser/MarkdownParserBridge.mm +4 -0
  257. package/ios/renderer/AttributedRenderer.m +20 -2
  258. package/ios/renderer/BaseRenderer.h +17 -2
  259. package/ios/renderer/BaseRenderer.m +30 -0
  260. package/ios/renderer/BlockquoteRenderer.h +1 -1
  261. package/ios/renderer/BlockquoteRenderer.m +144 -35
  262. package/ios/renderer/CodeBlockRenderer.h +1 -1
  263. package/ios/renderer/CodeBlockRenderer.m +27 -23
  264. package/ios/renderer/CodeRenderer.h +1 -1
  265. package/ios/renderer/CodeRenderer.m +3 -1
  266. package/ios/renderer/ENRMImageRenderer.h +1 -1
  267. package/ios/renderer/ENRMImageRenderer.m +5 -1
  268. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  269. package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
  270. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  271. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  272. package/ios/renderer/EmphasisRenderer.h +1 -1
  273. package/ios/renderer/EmphasisRenderer.m +3 -1
  274. package/ios/renderer/HeadingRenderer.h +1 -1
  275. package/ios/renderer/HeadingRenderer.m +13 -6
  276. package/ios/renderer/HighlightRenderer.h +1 -1
  277. package/ios/renderer/HighlightRenderer.m +3 -1
  278. package/ios/renderer/LinkRenderer.h +1 -1
  279. package/ios/renderer/LinkRenderer.m +3 -1
  280. package/ios/renderer/ListItemRenderer.h +13 -1
  281. package/ios/renderer/ListItemRenderer.m +102 -44
  282. package/ios/renderer/ListRenderer.h +2 -2
  283. package/ios/renderer/ListRenderer.m +63 -25
  284. package/ios/renderer/ParagraphRenderer.h +1 -1
  285. package/ios/renderer/ParagraphRenderer.m +16 -8
  286. package/ios/renderer/RenderContext.h +10 -0
  287. package/ios/renderer/RenderContext.m +44 -0
  288. package/ios/renderer/RendererFactory.m +14 -0
  289. package/ios/renderer/StrikethroughRenderer.h +1 -1
  290. package/ios/renderer/StrikethroughRenderer.m +3 -1
  291. package/ios/renderer/StrongRenderer.h +1 -1
  292. package/ios/renderer/StrongRenderer.m +3 -1
  293. package/ios/renderer/SubscriptRenderer.h +1 -1
  294. package/ios/renderer/SubscriptRenderer.m +3 -1
  295. package/ios/renderer/SuperscriptRenderer.h +1 -1
  296. package/ios/renderer/SuperscriptRenderer.m +3 -1
  297. package/ios/renderer/TextRenderer.h +2 -2
  298. package/ios/renderer/TextRenderer.m +3 -1
  299. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  300. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  301. package/ios/renderer/UnderlineRenderer.h +1 -1
  302. package/ios/renderer/UnderlineRenderer.m +3 -1
  303. package/ios/styles/StyleConfig.h +22 -0
  304. package/ios/styles/StyleConfig.mm +132 -0
  305. package/ios/utils/BlockquoteBorder.h +6 -0
  306. package/ios/utils/BlockquoteBorder.m +249 -52
  307. package/ios/utils/CodeBlockBackground.m +16 -2
  308. package/ios/utils/ENRMAtomicSize.h +65 -0
  309. package/ios/utils/ENRMBlockMenu.h +57 -0
  310. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  311. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  312. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  313. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  314. package/ios/utils/ENRMFeatureFlags.h +7 -0
  315. package/ios/utils/ENRMImageDownloader.h +11 -1
  316. package/ios/utils/ENRMImageDownloader.m +60 -16
  317. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  318. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  319. package/ios/utils/ENRMMathFallback.h +26 -0
  320. package/ios/utils/ENRMMathFallback.m +18 -0
  321. package/ios/utils/ENRMTextViewSetup.h +35 -17
  322. package/ios/utils/EditMenuUtils.h +22 -5
  323. package/ios/utils/EditMenuUtils.m +53 -9
  324. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  325. package/ios/utils/LastElementUtils.h +1 -0
  326. package/ios/utils/ListMarkerDrawer.m +29 -42
  327. package/ios/utils/MarkdownASTSerializer.m +4 -0
  328. package/ios/utils/ParagraphStyleUtils.h +5 -0
  329. package/ios/utils/ParagraphStyleUtils.m +77 -17
  330. package/ios/utils/RenderedMarkdownSegment.h +13 -1
  331. package/ios/utils/RenderedMarkdownSegment.m +20 -0
  332. package/ios/utils/SegmentRenderer.m +17 -4
  333. package/ios/utils/StreamingMarkdownFilter.h +11 -1
  334. package/ios/utils/StreamingMarkdownFilter.m +98 -2
  335. package/ios/utils/StylePropsUtils.h +63 -0
  336. package/ios/utils/TaskListTapUtils.m +24 -0
  337. package/ios/views/ENRMCodeBlockContainerView.h +53 -0
  338. package/ios/views/ENRMCodeBlockContainerView.m +559 -0
  339. package/ios/views/ENRMMathContainerView.h +16 -0
  340. package/ios/views/ENRMMathContainerView.m +74 -19
  341. package/ios/views/TableContainerView.h +22 -0
  342. package/ios/views/TableContainerView.m +261 -175
  343. package/jest.d.ts +1 -0
  344. package/jest.js +5 -0
  345. package/lib/module/EnrichedMarkdownNativeComponent.ts +73 -0
  346. package/lib/module/EnrichedMarkdownTextInput.js +84 -5
  347. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  348. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  349. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +74 -0
  350. package/lib/module/headingDefaults.js +34 -0
  351. package/lib/module/headingDefaults.js.map +1 -0
  352. package/lib/module/index.js.map +1 -1
  353. package/lib/module/jest/index.js +191 -0
  354. package/lib/module/jest/index.js.map +1 -0
  355. package/lib/module/native/EnrichedMarkdownText.js +37 -5
  356. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  357. package/lib/module/normalizeMarkdownStyle.js +81 -19
  358. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  359. package/lib/module/normalizeMarkdownStyle.web.js +56 -18
  360. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  361. package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
  362. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  363. package/lib/module/normalizeMenuItem.js +1 -1
  364. package/lib/module/utils/textInputState.js +26 -0
  365. package/lib/module/utils/textInputState.js.map +1 -0
  366. package/lib/module/web/EnrichedMarkdownText.js +9 -5
  367. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  368. package/lib/module/web/parseMarkdown.js +4 -3
  369. package/lib/module/web/parseMarkdown.js.map +1 -1
  370. package/lib/module/web/renderers/InlineRenderers.js +6 -0
  371. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  372. package/lib/module/web/renderers/ListRenderers.js +7 -2
  373. package/lib/module/web/renderers/ListRenderers.js.map +1 -1
  374. package/lib/module/web/renderers/index.js +7 -4
  375. package/lib/module/web/renderers/index.js.map +1 -1
  376. package/lib/module/web/styles.js +83 -6
  377. package/lib/module/web/styles.js.map +1 -1
  378. package/lib/module/web/utils.js +1 -0
  379. package/lib/module/web/utils.js.map +1 -1
  380. package/lib/module/web/wasm/md4c.js +0 -0
  381. package/lib/module/web/wasm/md4c.js.map +1 -1
  382. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +66 -0
  383. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  384. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
  385. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  386. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
  387. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  388. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +66 -0
  389. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  390. package/lib/typescript/src/headingDefaults.d.ts +9 -0
  391. package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
  392. package/lib/typescript/src/index.d.ts +2 -2
  393. package/lib/typescript/src/index.d.ts.map +1 -1
  394. package/lib/typescript/src/index.web.d.ts +1 -1
  395. package/lib/typescript/src/index.web.d.ts.map +1 -1
  396. package/lib/typescript/src/jest/index.d.ts +6 -0
  397. package/lib/typescript/src/jest/index.d.ts.map +1 -0
  398. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
  399. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  400. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  401. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  402. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
  403. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  404. package/lib/typescript/src/types/MarkdownStyle.d.ts +72 -0
  405. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  406. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +27 -0
  407. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  408. package/lib/typescript/src/types/MarkdownTextProps.d.ts +104 -9
  409. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  410. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +19 -0
  411. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  412. package/lib/typescript/src/types/events.d.ts +19 -0
  413. package/lib/typescript/src/types/events.d.ts.map +1 -1
  414. package/lib/typescript/src/utils/textInputState.d.ts +24 -0
  415. package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
  416. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  417. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  418. package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
  419. package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
  420. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  421. package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
  422. package/lib/typescript/src/web/renderers/index.d.ts +2 -1
  423. package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
  424. package/lib/typescript/src/web/styles.d.ts +2 -1
  425. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  426. package/lib/typescript/src/web/types.d.ts +6 -2
  427. package/lib/typescript/src/web/types.d.ts.map +1 -1
  428. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  429. package/package.json +36 -9
  430. package/postinstall.mjs +131 -0
  431. package/ratex-version.json +21 -0
  432. package/src/EnrichedMarkdownNativeComponent.ts +73 -0
  433. package/src/EnrichedMarkdownTextInput.tsx +159 -8
  434. package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  435. package/src/EnrichedMarkdownTextNativeComponent.ts +74 -0
  436. package/src/headingDefaults.ts +21 -0
  437. package/src/index.tsx +9 -0
  438. package/src/index.web.tsx +1 -0
  439. package/src/jest/index.tsx +215 -0
  440. package/src/native/EnrichedMarkdownText.tsx +53 -2
  441. package/src/normalizeMarkdownStyle.ts +102 -16
  442. package/src/normalizeMarkdownStyle.web.ts +63 -15
  443. package/src/normalizeMarkdownTextInputStyle.ts +110 -35
  444. package/src/normalizeMenuItem.ts +1 -1
  445. package/src/types/MarkdownStyle.ts +74 -0
  446. package/src/types/MarkdownStyleInternal.ts +39 -0
  447. package/src/types/MarkdownTextProps.ts +101 -9
  448. package/src/types/MarkdownTextProps.web.ts +19 -0
  449. package/src/types/events.ts +21 -0
  450. package/src/utils/textInputState.ts +35 -0
  451. package/src/web/EnrichedMarkdownText.tsx +16 -2
  452. package/src/web/parseMarkdown.ts +6 -2
  453. package/src/web/renderers/InlineRenderers.tsx +5 -0
  454. package/src/web/renderers/ListRenderers.tsx +14 -2
  455. package/src/web/renderers/index.tsx +6 -2
  456. package/src/web/styles.ts +89 -5
  457. package/src/web/types.ts +6 -1
  458. package/src/web/utils.ts +1 -0
  459. package/src/web/wasm/md4c.js +0 -0
  460. package/vendor-grammars.mjs +511 -0
  461. package/vendor-ratex.mjs +173 -0
  462. package/app.plugin.js +0 -2
  463. package/plugin/build/withAndroidMath.js +0 -19
  464. package/plugin/build/withIosMath.js +0 -28
  465. package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
  466. package/plugin/src/withAndroidMath.ts +0 -24
  467. package/plugin/src/withIosMath.ts +0 -35
  468. package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
  469. package/plugin/tsconfig.build.json +0 -16
@@ -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) {
@@ -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(
@@ -32,6 +32,12 @@ data class SelectionMenuConfig(
32
32
  // Plural templates indexed by image count (0..100). Empty when no pluralLabels
33
33
  // are set; counts > 100 use copyImageUrlsLabel (the "other" form).
34
34
  val copyImageUrlPluralTemplates: List<String> = emptyList(),
35
+ val copyEnabled: Boolean = true,
36
+ // Copy writes Markdown (plain text) instead of plain + HTML / raw block content.
37
+ val copyFormatMarkdown: Boolean = false,
38
+ // Drop every system-provided item (Select all, Share, text assist, PROCESS_TEXT…).
39
+ val systemItemsNone: Boolean = false,
40
+ val codeBlockCopyButton: Boolean = true,
35
41
  )
36
42
 
37
43
  /**
@@ -57,6 +63,10 @@ fun createSelectionActionModeCallback(
57
63
  textView: TextView,
58
64
  getCustomItemTexts: () -> List<String> = { emptyList() },
59
65
  getSelectionMenuConfig: () -> SelectionMenuConfig = { SelectionMenuConfig() },
66
+ isContextMenuHidden: () -> Boolean = { false },
67
+ // Lets the owning view hold the live ActionMode so it can invalidate() the
68
+ // toolbar when a prop changes while the menu is already on screen.
69
+ onActionModeChanged: (mode: ActionMode?) -> Unit = {},
60
70
  onCustomItemPress: (itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit =
61
71
  { _, _, _, _ -> },
62
72
  ): ActionMode.Callback =
@@ -64,7 +74,10 @@ fun createSelectionActionModeCallback(
64
74
  override fun onCreateActionMode(
65
75
  mode: ActionMode?,
66
76
  menu: Menu?,
67
- ): Boolean = true
77
+ ): Boolean {
78
+ onActionModeChanged(mode)
79
+ return true
80
+ }
68
81
 
69
82
  override fun onPrepareActionMode(
70
83
  mode: ActionMode?,
@@ -72,24 +85,39 @@ fun createSelectionActionModeCallback(
72
85
  ): Boolean {
73
86
  if (menu == null) return false
74
87
 
88
+ // Keep the ActionMode alive (selection handles and highlighting stay
89
+ // usable, which is what TalkBack relies on) but ship an empty menu so
90
+ // the floating toolbar has nothing to show.
91
+ if (isContextMenuHidden()) {
92
+ menu.clear()
93
+ return true
94
+ }
95
+
75
96
  menu.removeItem(MENU_ITEM_COPY_MARKDOWN)
76
97
  menu.removeItem(MENU_ITEM_COPY_IMAGE_URL)
77
98
  menu.removeGroup(MENU_ITEM_CUSTOM_GROUP)
78
99
 
79
100
  val selectionMenuConfig = getSelectionMenuConfig()
101
+ val hasSelection = textView.selectionStart >= 0 && textView.selectionEnd > textView.selectionStart
80
102
 
81
- // The system Copy item is added by the platform; relabel it so it
82
- // matches the rest of the localized menu (iOS rebuilds the menu so
83
- // gets this for free; Android only intercepts the click).
84
- if (selectionMenuConfig.copyLabel.isNotEmpty()) {
103
+ if (selectionMenuConfig.systemItemsNone) {
104
+ // Editor re-adds Select all and the text-assist items before calling
105
+ // us on every prepare, so clear on each pass and put back only Copy.
106
+ // Its click is still intercepted in onActionItemClicked.
107
+ menu.clear()
108
+ if (selectionMenuConfig.copyEnabled && hasSelection) {
109
+ menu.add(Menu.NONE, android.R.id.copy, Menu.NONE, selectionMenuConfig.copyLabel)
110
+ }
111
+ } else if (!selectionMenuConfig.copyEnabled) {
112
+ menu.removeItem(android.R.id.copy)
113
+ } else if (selectionMenuConfig.copyLabel.isNotEmpty()) {
114
+ // The system Copy item is added by the platform; relabel it so it
115
+ // matches the rest of the localized menu (iOS rebuilds the menu so
116
+ // gets this for free; Android only intercepts the click).
85
117
  menu.findItem(android.R.id.copy)?.title = selectionMenuConfig.copyLabel
86
118
  }
87
119
 
88
- if (
89
- selectionMenuConfig.copyAsMarkdown &&
90
- textView.selectionStart >= 0 &&
91
- textView.selectionEnd > textView.selectionStart
92
- ) {
120
+ if (selectionMenuConfig.copyAsMarkdown && hasSelection) {
93
121
  menu.add(
94
122
  Menu.NONE,
95
123
  MENU_ITEM_COPY_MARKDOWN,
@@ -98,7 +126,7 @@ fun createSelectionActionModeCallback(
98
126
  )
99
127
  }
100
128
 
101
- if (textView.selectionStart >= 0 && textView.selectionEnd > textView.selectionStart) {
129
+ if (hasSelection) {
102
130
  val customItems = getCustomItemTexts()
103
131
  customItems.forEachIndexed { index, text ->
104
132
  menu
@@ -129,7 +157,9 @@ fun createSelectionActionModeCallback(
129
157
 
130
158
  when (itemId) {
131
159
  android.R.id.copy -> {
132
- textView.copyWithHTML()
160
+ if (!getSelectionMenuConfig().copyFormatMarkdown || !textView.copyMarkdownToClipboard()) {
161
+ textView.copyWithHTML()
162
+ }
133
163
  mode?.finish()
134
164
  return true
135
165
  }
@@ -161,7 +191,9 @@ fun createSelectionActionModeCallback(
161
191
  return false
162
192
  }
163
193
 
164
- override fun onDestroyActionMode(mode: ActionMode?) {}
194
+ override fun onDestroyActionMode(mode: ActionMode?) {
195
+ onActionModeChanged(null)
196
+ }
165
197
  }
166
198
 
167
199
  /** Copies selection as both plain text and HTML with inline styles. */
@@ -197,10 +229,12 @@ private fun TextView.copyWithHTML() {
197
229
  }
198
230
  }
199
231
 
200
- private fun TextView.copyMarkdownToClipboard() {
201
- val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: return
232
+ /** Returns false when the selection has no Markdown to copy. */
233
+ private fun TextView.copyMarkdownToClipboard(): Boolean {
234
+ val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: return false
202
235
  val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
203
236
  clipboard.setPrimaryClip(ClipData.newPlainText("Markdown", markdown))
237
+ return true
204
238
  }
205
239
 
206
240
  /** Returns remote image URLs (http/https only) from the current selection. */
@@ -0,0 +1,45 @@
1
+ package com.swmansion.enriched.markdown.views
2
+
3
+ import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
4
+
5
+ /**
6
+ * Selection-menu options that table, math and code block views need for their
7
+ * own long-press menus. Every default keeps the legacy behavior, so a block view
8
+ * that has not received options yet behaves exactly as before.
9
+ */
10
+ data class BlockMenuOptions(
11
+ val copyHidden: Boolean = false,
12
+ // The Copy item writes the block's Markdown instead of its raw content.
13
+ val copyWritesMarkdown: Boolean = false,
14
+ val copyAsMarkdownHidden: Boolean = false,
15
+ // Hides the copy button in the code block header.
16
+ val copyButtonHidden: Boolean = false,
17
+ ) {
18
+ val hasItems: Boolean get() = !copyHidden || !copyAsMarkdownHidden
19
+
20
+ companion object {
21
+ fun from(config: SelectionMenuConfig) =
22
+ BlockMenuOptions(
23
+ copyHidden = !config.copyEnabled,
24
+ copyWritesMarkdown = config.copyFormatMarkdown,
25
+ copyAsMarkdownHidden = !config.copyAsMarkdown,
26
+ copyButtonHidden = !config.codeBlockCopyButton,
27
+ )
28
+ }
29
+ }
30
+
31
+ /** Adds the Copy / Copy as Markdown items shared by the table, math and code block menus. */
32
+ fun ContextMenuPopup.Builder.blockCopyItems(
33
+ options: BlockMenuOptions,
34
+ copyLabel: String,
35
+ copyAsMarkdownLabel: String,
36
+ copyRaw: () -> Unit,
37
+ copyMarkdown: () -> Unit,
38
+ ) {
39
+ if (!options.copyHidden) {
40
+ item(ContextMenuPopup.Icon.COPY, copyLabel, if (options.copyWritesMarkdown) copyMarkdown else copyRaw)
41
+ }
42
+ if (!options.copyAsMarkdownHidden) {
43
+ item(ContextMenuPopup.Icon.DOCUMENT, copyAsMarkdownLabel, copyMarkdown)
44
+ }
45
+ }