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,7 @@ import android.widget.TextView
12
12
  import com.swmansion.enriched.markdown.EnrichedMarkdown
13
13
  import com.swmansion.enriched.markdown.EnrichedMarkdownText
14
14
  import com.swmansion.enriched.markdown.spans.ImageSpan
15
+ import com.swmansion.enriched.markdown.spans.LinkSpacers
15
16
  import com.swmansion.enriched.markdown.styles.StyleConfig
16
17
  import com.swmansion.enriched.markdown.utils.common.layout.isLayoutRTL
17
18
  import com.swmansion.enriched.markdown.utils.text.conversion.HTMLGenerator
@@ -32,6 +33,14 @@ data class SelectionMenuConfig(
32
33
  // Plural templates indexed by image count (0..100). Empty when no pluralLabels
33
34
  // are set; counts > 100 use copyImageUrlsLabel (the "other" form).
34
35
  val copyImageUrlPluralTemplates: List<String> = emptyList(),
36
+ val copyEnabled: Boolean = true,
37
+ // Copy writes Markdown (plain text) instead of plain + HTML / raw block content.
38
+ val copyFormatMarkdown: Boolean = false,
39
+ // Drop every system-provided item (Select all, Share, text assist, PROCESS_TEXT…).
40
+ val systemItemsNone: Boolean = false,
41
+ val codeBlockCopyButton: Boolean = true,
42
+ // Copy hands the selection to onCopyRequest instead of writing the clipboard.
43
+ val interceptCopy: Boolean = false,
35
44
  )
36
45
 
37
46
  /**
@@ -57,14 +66,24 @@ fun createSelectionActionModeCallback(
57
66
  textView: TextView,
58
67
  getCustomItemTexts: () -> List<String> = { emptyList() },
59
68
  getSelectionMenuConfig: () -> SelectionMenuConfig = { SelectionMenuConfig() },
69
+ isContextMenuHidden: () -> Boolean = { false },
70
+ // Lets the owning view hold the live ActionMode so it can invalidate() the
71
+ // toolbar when a prop changes while the menu is already on screen.
72
+ onActionModeChanged: (mode: ActionMode?) -> Unit = {},
60
73
  onCustomItemPress: (itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit =
61
74
  { _, _, _, _ -> },
75
+ // Receives the intercepted Copy when `interceptCopy` is set. `fullSelection`
76
+ // is whether the selection spans the whole text view.
77
+ onCopyRequest: (markdown: String, text: String, fullSelection: Boolean) -> Unit = { _, _, _ -> },
62
78
  ): ActionMode.Callback =
63
79
  object : ActionMode.Callback {
64
80
  override fun onCreateActionMode(
65
81
  mode: ActionMode?,
66
82
  menu: Menu?,
67
- ): Boolean = true
83
+ ): Boolean {
84
+ onActionModeChanged(mode)
85
+ return true
86
+ }
68
87
 
69
88
  override fun onPrepareActionMode(
70
89
  mode: ActionMode?,
@@ -72,24 +91,39 @@ fun createSelectionActionModeCallback(
72
91
  ): Boolean {
73
92
  if (menu == null) return false
74
93
 
94
+ // Keep the ActionMode alive (selection handles and highlighting stay
95
+ // usable, which is what TalkBack relies on) but ship an empty menu so
96
+ // the floating toolbar has nothing to show.
97
+ if (isContextMenuHidden()) {
98
+ menu.clear()
99
+ return true
100
+ }
101
+
75
102
  menu.removeItem(MENU_ITEM_COPY_MARKDOWN)
76
103
  menu.removeItem(MENU_ITEM_COPY_IMAGE_URL)
77
104
  menu.removeGroup(MENU_ITEM_CUSTOM_GROUP)
78
105
 
79
106
  val selectionMenuConfig = getSelectionMenuConfig()
107
+ val hasSelection = textView.selectionStart >= 0 && textView.selectionEnd > textView.selectionStart
80
108
 
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()) {
109
+ if (selectionMenuConfig.systemItemsNone) {
110
+ // Editor re-adds Select all and the text-assist items before calling
111
+ // us on every prepare, so clear on each pass and put back only Copy.
112
+ // Its click is still intercepted in onActionItemClicked.
113
+ menu.clear()
114
+ if (selectionMenuConfig.copyEnabled && hasSelection) {
115
+ menu.add(Menu.NONE, android.R.id.copy, Menu.NONE, selectionMenuConfig.copyLabel)
116
+ }
117
+ } else if (!selectionMenuConfig.copyEnabled) {
118
+ menu.removeItem(android.R.id.copy)
119
+ } else if (selectionMenuConfig.copyLabel.isNotEmpty()) {
120
+ // The system Copy item is added by the platform; relabel it so it
121
+ // matches the rest of the localized menu (iOS rebuilds the menu so
122
+ // gets this for free; Android only intercepts the click).
85
123
  menu.findItem(android.R.id.copy)?.title = selectionMenuConfig.copyLabel
86
124
  }
87
125
 
88
- if (
89
- selectionMenuConfig.copyAsMarkdown &&
90
- textView.selectionStart >= 0 &&
91
- textView.selectionEnd > textView.selectionStart
92
- ) {
126
+ if (selectionMenuConfig.copyAsMarkdown && hasSelection) {
93
127
  menu.add(
94
128
  Menu.NONE,
95
129
  MENU_ITEM_COPY_MARKDOWN,
@@ -98,7 +132,7 @@ fun createSelectionActionModeCallback(
98
132
  )
99
133
  }
100
134
 
101
- if (textView.selectionStart >= 0 && textView.selectionEnd > textView.selectionStart) {
135
+ if (hasSelection) {
102
136
  val customItems = getCustomItemTexts()
103
137
  customItems.forEachIndexed { index, text ->
104
138
  menu
@@ -129,7 +163,12 @@ fun createSelectionActionModeCallback(
129
163
 
130
164
  when (itemId) {
131
165
  android.R.id.copy -> {
132
- textView.copyWithHTML()
166
+ val config = getSelectionMenuConfig()
167
+ if (config.interceptCopy) {
168
+ textView.requestCopy(onCopyRequest)
169
+ } else if (!config.copyFormatMarkdown || !textView.copyMarkdownToClipboard()) {
170
+ textView.copyWithHTML()
171
+ }
133
172
  mode?.finish()
134
173
  return true
135
174
  }
@@ -161,7 +200,9 @@ fun createSelectionActionModeCallback(
161
200
  return false
162
201
  }
163
202
 
164
- override fun onDestroyActionMode(mode: ActionMode?) {}
203
+ override fun onDestroyActionMode(mode: ActionMode?) {
204
+ onActionModeChanged(null)
205
+ }
165
206
  }
166
207
 
167
208
  /** Copies selection as both plain text and HTML with inline styles. */
@@ -171,7 +212,8 @@ private fun TextView.copyWithHTML() {
171
212
  if (start < 0 || end < 0 || start >= end) return
172
213
 
173
214
  val spannable = text as? Spannable ?: return
174
- val selectedText = spannable.subSequence(start, end)
215
+ // Link spacers only reserve layout space; keep them out of every copied flavor.
216
+ val selectedText = LinkSpacers.strip(spannable.subSequence(start, end))
175
217
  val plainText = selectedText.toString()
176
218
 
177
219
  val styleConfig =
@@ -197,10 +239,24 @@ private fun TextView.copyWithHTML() {
197
239
  }
198
240
  }
199
241
 
200
- private fun TextView.copyMarkdownToClipboard() {
201
- val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: return
242
+ /** Hands the selection to [onCopyRequest] without touching the clipboard. */
243
+ private fun TextView.requestCopy(onCopyRequest: (markdown: String, text: String, fullSelection: Boolean) -> Unit) {
244
+ val start = selectionStart
245
+ val end = selectionEnd
246
+ if (start < 0 || end < 0 || start >= end) return
247
+
248
+ val selectedText = LinkSpacers.strip(text.subSequence(start, end)).toString()
249
+ val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: selectedText
250
+ // Same threshold MarkdownExtractor uses to return the original markdown.
251
+ onCopyRequest(markdown, selectedText, start == 0 && end >= text.length - 1)
252
+ }
253
+
254
+ /** Returns false when the selection has no Markdown to copy. */
255
+ private fun TextView.copyMarkdownToClipboard(): Boolean {
256
+ val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: return false
202
257
  val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
203
258
  clipboard.setPrimaryClip(ClipData.newPlainText("Markdown", markdown))
259
+ return true
204
260
  }
205
261
 
206
262
  /** Returns remote image URLs (http/https only) from the current selection. */
@@ -0,0 +1,232 @@
1
+ package com.swmansion.enriched.markdown.utils.text.view
2
+
3
+ import android.text.Layout
4
+ import android.text.Selection
5
+ import android.text.Spannable
6
+ import android.view.View
7
+ import android.widget.TextView
8
+ import com.facebook.react.uimanager.PixelUtil
9
+ import com.swmansion.enriched.markdown.spans.LinkSpacers
10
+ import com.swmansion.enriched.markdown.utils.text.conversion.MarkdownExtractor
11
+ import kotlin.math.max
12
+ import kotlin.math.min
13
+
14
+ /** A rect in dp, relative to the reporting view. */
15
+ data class SelectionRect(
16
+ val x: Float,
17
+ val y: Float,
18
+ val width: Float,
19
+ val height: Float,
20
+ ) {
21
+ companion object {
22
+ val ZERO = SelectionRect(0f, 0f, 0f, 0f)
23
+
24
+ /** Converts a px rect (already in the reporting view's coordinates) to dp. */
25
+ fun fromPx(
26
+ left: Float,
27
+ top: Float,
28
+ right: Float,
29
+ bottom: Float,
30
+ ) = SelectionRect(
31
+ PixelUtil.toDIPFromPixel(min(left, right)),
32
+ PixelUtil.toDIPFromPixel(min(top, bottom)),
33
+ PixelUtil.toDIPFromPixel(kotlin.math.abs(right - left)),
34
+ PixelUtil.toDIPFromPixel(kotlin.math.abs(bottom - top)),
35
+ )
36
+ }
37
+ }
38
+
39
+ /** Payload of `onSelectionChange`. Compared structurally for deduplication. */
40
+ data class SelectionSnapshot(
41
+ val active: Boolean,
42
+ val source: String,
43
+ val text: String,
44
+ val markdown: String,
45
+ val whole: Boolean,
46
+ val rect: SelectionRect,
47
+ val startRect: SelectionRect,
48
+ val endRect: SelectionRect,
49
+ // Not tracked on Android: the selection is reported continuously while a handle is dragged.
50
+ val adjusting: Boolean = false,
51
+ ) {
52
+ companion object {
53
+ val INACTIVE =
54
+ SelectionSnapshot(false, "selection", "", "", false, SelectionRect.ZERO, SelectionRect.ZERO, SelectionRect.ZERO)
55
+
56
+ /** A long-pressed block: `whole`, and every rect is the block's frame. */
57
+ fun block(
58
+ source: String,
59
+ markdown: String,
60
+ text: String,
61
+ rect: SelectionRect,
62
+ ) = SelectionSnapshot(true, source, text, markdown, true, rect, rect, rect)
63
+ }
64
+ }
65
+
66
+ /** A text view's current selection, before the owner applies its `whole` rule. */
67
+ data class TextSelection(
68
+ val text: String,
69
+ val markdown: String,
70
+ // Same threshold MarkdownExtractor uses to return the original markdown.
71
+ val fullSelection: Boolean,
72
+ val rect: SelectionRect,
73
+ val startRect: SelectionRect,
74
+ val endRect: SelectionRect,
75
+ )
76
+
77
+ /**
78
+ * Reads the selection of this text view with its rects offset by [offsetXPx]/[offsetYPx]
79
+ * (a segment's position inside its container). `rect` spans the full content width when the
80
+ * selection covers several lines; `startRect`/`endRect` cover the selected part of the first
81
+ * and last line. Returns null when nothing is selected.
82
+ */
83
+ fun TextView.currentTextSelection(
84
+ offsetXPx: Int = 0,
85
+ offsetYPx: Int = 0,
86
+ ): TextSelection? {
87
+ val start = selectionStart
88
+ val end = selectionEnd
89
+ if (start < 0 || end <= start) return null
90
+ val layout = layout ?: return null
91
+ val content = text ?: return null
92
+
93
+ val selectedText = LinkSpacers.strip(content.subSequence(start, end)).toString()
94
+ val markdown = MarkdownExtractor.getMarkdownForSelection(this) ?: selectedText
95
+
96
+ val originX = (offsetXPx + totalPaddingLeft - scrollX).toFloat()
97
+ val originY = (offsetYPx + totalPaddingTop - scrollY).toFloat()
98
+
99
+ val startLine = layout.getLineForOffset(start)
100
+ // An end at a line break belongs to the line it closes, not the next one.
101
+ val endLine = layout.getLineForOffset(max(start, end - 1))
102
+ val startX = layout.getPrimaryHorizontal(start)
103
+ val endX = lineEndHorizontal(layout, endLine, end)
104
+
105
+ fun lineRect(
106
+ line: Int,
107
+ left: Float,
108
+ right: Float,
109
+ ) = SelectionRect.fromPx(
110
+ left + originX,
111
+ layout.getLineTop(line) + originY,
112
+ right + originX,
113
+ layout.getLineBottom(line) + originY,
114
+ )
115
+
116
+ val startRect: SelectionRect
117
+ val endRect: SelectionRect
118
+ val rect: SelectionRect
119
+ if (startLine == endLine) {
120
+ startRect = lineRect(startLine, startX, endX)
121
+ endRect = startRect
122
+ rect = startRect
123
+ } else {
124
+ startRect = lineRect(startLine, startX, layout.getLineRight(startLine))
125
+ endRect = lineRect(endLine, layout.getLineLeft(endLine), endX)
126
+ rect =
127
+ SelectionRect.fromPx(
128
+ originX,
129
+ layout.getLineTop(startLine) + originY,
130
+ layout.width + originX,
131
+ layout.getLineBottom(endLine) + originY,
132
+ )
133
+ }
134
+
135
+ return TextSelection(
136
+ text = selectedText,
137
+ markdown = markdown,
138
+ fullSelection = start == 0 && end >= content.length - 1,
139
+ rect = rect,
140
+ startRect = startRect,
141
+ endRect = endRect,
142
+ )
143
+ }
144
+
145
+ /** Clears this text view's selection, which also ends its selection ActionMode. */
146
+ fun TextView.clearTextSelection() {
147
+ val spannable = text as? Spannable ?: return
148
+ Selection.removeSelection(spannable)
149
+ }
150
+
151
+ /**
152
+ * x of [offset] on [line]. At a line's end offset `getPrimaryHorizontal` reports the start
153
+ * of the next line, so a selection ending there uses the line's right edge.
154
+ */
155
+ private fun lineEndHorizontal(
156
+ layout: Layout,
157
+ line: Int,
158
+ offset: Int,
159
+ ): Float =
160
+ if (offset >= layout.getLineEnd(line) && line < layout.lineCount - 1) {
161
+ layout.getLineRight(line)
162
+ } else {
163
+ layout.getPrimaryHorizontal(offset)
164
+ }
165
+
166
+ /**
167
+ * Coalesces selection changes into at most one `onSelectionChange` per frame. The payload is
168
+ * read when the frame fires, so the latest state always reaches JS; a cleared selection is
169
+ * reported right away and drops any pending update. Identical payloads are sent once, so the
170
+ * `setText` passes of streaming don't repeat `active: false`.
171
+ */
172
+ class SelectionChangeReporter(
173
+ private val view: View,
174
+ private val snapshot: () -> SelectionSnapshot,
175
+ private val emit: (SelectionSnapshot) -> Unit,
176
+ ) {
177
+ var enabled = false
178
+ set(value) {
179
+ if (field == value) return
180
+ field = value
181
+ cancel()
182
+ lastReported = SelectionSnapshot.INACTIVE
183
+ if (value) schedule()
184
+ }
185
+
186
+ private var lastReported = SelectionSnapshot.INACTIVE
187
+ private var pending = false
188
+ private val flushRunnable =
189
+ Runnable {
190
+ pending = false
191
+ report()
192
+ }
193
+
194
+ /** Reports the current state on the next frame. */
195
+ fun schedule() {
196
+ if (!enabled || pending) return
197
+ pending = true
198
+ view.postOnAnimation(flushRunnable)
199
+ }
200
+
201
+ fun onSelectionChanged(hasSelection: Boolean) {
202
+ if (hasSelection) schedule() else flushNow()
203
+ }
204
+
205
+ /** Reports the current state right away, dropping any pending frame. */
206
+ fun flushNow() {
207
+ cancel()
208
+ report()
209
+ }
210
+
211
+ /**
212
+ * Makes the next report go out even if it equals the last one — a block long-pressed
213
+ * again is a new request for the app's menu, not a repeat.
214
+ */
215
+ fun forgetLastReported() {
216
+ lastReported = SelectionSnapshot.INACTIVE
217
+ }
218
+
219
+ fun cancel() {
220
+ if (!pending) return
221
+ pending = false
222
+ view.removeCallbacks(flushRunnable)
223
+ }
224
+
225
+ private fun report() {
226
+ if (!enabled) return
227
+ val current = snapshot()
228
+ if (current == lastReported) return
229
+ lastReported = current
230
+ emit(current)
231
+ }
232
+ }
@@ -0,0 +1,64 @@
1
+ package com.swmansion.enriched.markdown.views
2
+
3
+ import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
4
+
5
+ /** Receives an intercepted block Copy: the block's Markdown and its raw content. */
6
+ typealias BlockCopyRequest = (markdown: String, text: String) -> Unit
7
+
8
+ /**
9
+ * Selection-menu options that table, math and code block views need for their
10
+ * own long-press menus. Every default keeps the legacy behavior, so a block view
11
+ * that has not received options yet behaves exactly as before.
12
+ */
13
+ data class BlockMenuOptions(
14
+ val copyHidden: Boolean = false,
15
+ // The Copy item writes the block's Markdown instead of its raw content.
16
+ val copyWritesMarkdown: Boolean = false,
17
+ val copyAsMarkdownHidden: Boolean = false,
18
+ // Hides the copy button in the code block header.
19
+ val copyButtonHidden: Boolean = false,
20
+ // Set when `onCopy` is on: the Copy item hands the content here instead of
21
+ // writing the clipboard. The host binds it to the block's source.
22
+ val onCopyRequest: BlockCopyRequest? = null,
23
+ ) {
24
+ val hasItems: Boolean get() = !copyHidden || !copyAsMarkdownHidden
25
+
26
+ companion object {
27
+ fun from(
28
+ config: SelectionMenuConfig,
29
+ onCopyRequest: BlockCopyRequest? = null,
30
+ ) = BlockMenuOptions(
31
+ copyHidden = !config.copyEnabled,
32
+ copyWritesMarkdown = config.copyFormatMarkdown,
33
+ copyAsMarkdownHidden = !config.copyAsMarkdown,
34
+ copyButtonHidden = !config.codeBlockCopyButton,
35
+ onCopyRequest = onCopyRequest.takeIf { config.interceptCopy },
36
+ )
37
+ }
38
+ }
39
+
40
+ /** Adds the Copy / Copy as Markdown items shared by the table, math and code block menus. */
41
+ fun ContextMenuPopup.Builder.blockCopyItems(
42
+ options: BlockMenuOptions,
43
+ copyLabel: String,
44
+ copyAsMarkdownLabel: String,
45
+ // The block's Markdown and raw content, read only for an intercepted Copy.
46
+ markdown: () -> String,
47
+ rawText: () -> String,
48
+ copyRaw: () -> Unit,
49
+ copyMarkdown: () -> Unit,
50
+ ) {
51
+ if (!options.copyHidden) {
52
+ val onCopyRequest = options.onCopyRequest
53
+ val onCopy =
54
+ when {
55
+ onCopyRequest != null -> { -> onCopyRequest(markdown(), rawText()) }
56
+ options.copyWritesMarkdown -> copyMarkdown
57
+ else -> copyRaw
58
+ }
59
+ item(ContextMenuPopup.Icon.COPY, copyLabel, onCopy)
60
+ }
61
+ if (!options.copyAsMarkdownHidden) {
62
+ item(ContextMenuPopup.Icon.DOCUMENT, copyAsMarkdownLabel, copyMarkdown)
63
+ }
64
+ }