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
@@ -2,6 +2,7 @@
2
2
  #import "ContextMenuUtils.h"
3
3
  #import "ENRMAccessibilityLabels.h"
4
4
  #import "ENRMAsyncRenderCoordinator.h"
5
+ #import "ENRMAtomicSize.h"
5
6
  #import "ENRMImageAttachment.h"
6
7
  #import "ENRMMarkdownParser.h"
7
8
  #import "ENRMTailFadeInAnimator.h"
@@ -10,12 +11,16 @@
10
11
  #import "ENRMTextViewSetup.h"
11
12
  #import "ENRMUIKit.h"
12
13
  #import "EditMenuUtils.h"
14
+ #import "ImageRequestHeaderUtils.h"
13
15
 
14
16
  #import "ENRMFeatureFlags.h"
15
17
 
16
18
  #if ENRICHED_MARKDOWN_MATH
17
19
  #import "ENRMMathContainerView.h"
18
20
  #endif
21
+ #import "ENRMCodeBlockContainerView.h"
22
+ #import "ENRMLinkLayout.h"
23
+ #import "ENRMSelectionChange.h"
19
24
  #import "ENRMSpoilerCapable.h"
20
25
  #import "ENRMSpoilerOverlayView.h"
21
26
  #import "ENRMSpoilerTapUtils.h"
@@ -66,10 +71,14 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
66
71
 
67
72
  static char kENRMSegmentFadeAnimatorKey;
68
73
 
69
- @interface EnrichedMarkdown () <RCTEnrichedMarkdownViewProtocol, UITextViewDelegate>
74
+ @interface EnrichedMarkdown () <RCTEnrichedMarkdownViewProtocol, UITextViewDelegate, ENRMImageLayoutObserver>
75
+ + (ENRMMd4cFlags *)flagsFromProps:(const EnrichedMarkdownMd4cFlagsStruct &)props;
70
76
  - (void)emitLinkPress:(NSString *)url;
71
77
  - (void)emitLinkLongPress:(NSString *)url;
78
+ - (void)emitLinkLayoutIfNeeded;
72
79
  - (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text;
80
+ - (void)emitCopyPress:(NSString *)code language:(NSString *)language;
81
+ - (void)emitCopyRequest:(NSString *)source markdown:(NSString *)markdown text:(NSString *)text whole:(BOOL)whole;
73
82
  - (void)emitContextMenuItemPress:(NSString *)itemText
74
83
  selectedText:(NSString *)selectedText
75
84
  selectionStart:(NSUInteger)selectionStart
@@ -98,8 +107,14 @@ static char kENRMSegmentFadeAnimatorKey;
98
107
  BOOL _allowTrailingMargin;
99
108
  BOOL _selectable;
100
109
  BOOL _enableLinkPreview;
110
+ BOOL _linkLongPressSelects;
111
+ BOOL _enableTaskListItemToggle;
112
+ BOOL _contextMenuHidden;
101
113
  BOOL _streamingAnimation;
102
114
  ENRMTableStreamingMode _tableStreamingMode;
115
+ ENRMCodeBlockStreamingMode _codeBlockStreamingMode;
116
+ // Trailing code block whose closing fence hasn't streamed in yet, if any.
117
+ ENRMCodeBlockSegment *_pendingCodeBlockSegment;
103
118
 
104
119
  size_t _renderedStyleFingerprint;
105
120
  size_t _pendingStyleFingerprint;
@@ -112,10 +127,27 @@ static char kENRMSegmentFadeAnimatorKey;
112
127
 
113
128
  ENRMSpoilerOverlay _spoilerOverlay;
114
129
 
130
+ BOOL _emitLinkLayout;
131
+ NSArray<NSDictionary *> *_lastLinkLayout;
132
+
133
+ ENRMSelectionChangeReporter *_selectionReporter;
134
+ #if !TARGET_OS_OSX
135
+ ENRMSelectionGestureTracker *_selectionGestures;
136
+ #endif
137
+ // At most one segment holds a selection; a new one clears the previous.
138
+ __weak ENRMPlatformTextView *_selectionOwner;
139
+ // A block long-pressed while contextMenuHidden is on, reported as the selection.
140
+ __weak RCTUIView *_activeBlockView;
141
+ NSString *_activeBlockSource;
142
+ NSString *_activeBlockMarkdown;
143
+ NSString *_activeBlockText;
144
+
115
145
  NSLineBreakStrategy _lineBreakStrategy;
116
146
 
117
147
  ENRMWritingDirectionMode _writingDirectionMode;
118
148
  NSWritingDirection _resolvedLayoutDirection;
149
+
150
+ ENRMAtomicSize _lastCommittedSize;
119
151
  }
120
152
 
121
153
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -123,43 +155,16 @@ static char kENRMSegmentFadeAnimatorKey;
123
155
  return concreteComponentDescriptorProvider<EnrichedMarkdownComponentDescriptor>();
124
156
  }
125
157
 
126
- /// Clears every piece of state that `updateProps` derives from props, back to the
127
- /// values the default-constructed `EnrichedMarkdownProps` describes.
128
- ///
129
- /// `updateProps` applies most props as a per-field diff against `_props`, so this
130
- /// state is only correct while it stays in phase with the `_props` baseline it was
131
- /// diffed from. `prepareForRecycle` resets `_props`, so it must reset this too —
132
- /// otherwise every field the next consumer leaves at its default silently inherits
133
- /// the previous consumer's value. Called from `initWithFrame:` as well, so the two
134
- /// baselines cannot drift apart.
135
- ///
136
- /// Requires `_fontScaleObserver` to already exist.
137
- - (void)resetPropDerivedState
138
- {
139
- // Discarded rather than cleared field-by-field: the style diff can only clear a
140
- // field when new and old differ, which never holds against a reset baseline.
141
- _config = nil;
142
-
143
- _md4cFlags = [ENRMMd4cFlags propsDefaultFlags];
144
- _maxFontSizeMultiplier = 0;
145
- _allowTrailingMargin = NO;
146
- _fontScaleObserver.allowFontScaling = YES;
147
- _streamingAnimation = NO;
148
- _tableStreamingMode = ENRMTableStreamingModeProgressive;
149
- _spoilerOverlay = ENRMSpoilerOverlayParticles;
150
- _lineBreakStrategy = NSLineBreakStrategyNone;
151
- _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
152
- _contextMenuItemTexts = nil;
153
- _contextMenuItemIcons = nil;
154
- _accessibilityLabels = nil;
155
-
156
- // Written only once a render completes, so stale until the next one lands.
157
- _renderedStyleFingerprint = 0;
158
- _pendingStyleFingerprint = 0;
159
-
160
- _dirtyFlags = ENRMDirtyNone;
161
- _heightUpdateCounter = 0;
162
- _state = nullptr;
158
+ + (ENRMMd4cFlags *)flagsFromProps:(const EnrichedMarkdownMd4cFlagsStruct &)props
159
+ {
160
+ ENRMMd4cFlags *flags = [ENRMMd4cFlags defaultFlags];
161
+ flags.underline = props.underline;
162
+ flags.superscript = props.superscript;
163
+ flags.subscript = props.subscript;
164
+ flags.latexMath = props.latexMath;
165
+ flags.highlight = props.highlight;
166
+ flags.hardSoftBreaks = props.hardSoftBreaks;
167
+ return flags;
163
168
  }
164
169
 
165
170
  - (instancetype)initWithFrame:(CGRect)frame
@@ -170,22 +175,31 @@ static char kENRMSegmentFadeAnimatorKey;
170
175
 
171
176
  self.backgroundColor = [RCTUIColor clearColor];
172
177
  _parser = [[ENRMMarkdownParser alloc] init];
178
+ _md4cFlags = [EnrichedMarkdown flagsFromProps:defaultProps->md4cFlags];
173
179
  _segmentViews = [NSMutableArray array];
174
180
  _segmentSignatures = [NSMutableArray array];
181
+ _dirtyFlags = ENRMDirtyNone;
175
182
  [self configureSegmentViewRegistry];
176
183
 
177
184
  _renderCoordinator =
178
185
  [[ENRMAsyncRenderCoordinator alloc] initWithQueueLabel:"com.swmansion.enriched.markdown.container.render"];
179
186
 
187
+ _maxFontSizeMultiplier = 0;
188
+ _allowTrailingMargin = NO;
180
189
  _selectable = YES;
181
190
  _enableLinkPreview = YES;
182
- _selectionMenuConfig = (ENRMSelectionMenuConfig){.copyAsMarkdown = YES, .copyImageURL = YES};
191
+ _enableTaskListItemToggle = YES;
192
+ _streamingAnimation = NO;
193
+ _tableStreamingMode = ENRMTableStreamingModeProgressive;
194
+ _codeBlockStreamingMode = ENRMCodeBlockStreamingModeProgressive;
195
+ _contextMenuHidden = NO;
196
+ _selectionMenuConfig = ENRMDefaultSelectionMenuConfig();
197
+ _lineBreakStrategy = NSLineBreakStrategyNone;
198
+ _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
183
199
  _resolvedLayoutDirection =
184
200
  [[RCTI18nUtil sharedInstance] isRTL] ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight;
185
201
 
186
202
  _fontScaleObserver = [[FontScaleObserver alloc] init];
187
- [self resetPropDerivedState];
188
-
189
203
  __weak EnrichedMarkdown *weakSelf = self;
190
204
  _fontScaleObserver.onChange = ^{
191
205
  EnrichedMarkdown *strongSelf = weakSelf;
@@ -199,10 +213,132 @@ static char kENRMSegmentFadeAnimatorKey;
199
213
  [strongSelf renderMarkdownContent:strongSelf->_cachedMarkdown];
200
214
  }
201
215
  };
216
+
217
+ _selectionReporter = [[ENRMSelectionChangeReporter alloc]
218
+ initWithSnapshot:^NSDictionary * {
219
+ EnrichedMarkdown *strongSelf = weakSelf;
220
+ return strongSelf ? [strongSelf selectionPayload] : ENRMInactiveSelectionPayload();
221
+ }
222
+ emit:^(NSDictionary *payload) {
223
+ EnrichedMarkdown *strongSelf = weakSelf;
224
+ if (!strongSelf)
225
+ return;
226
+ auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(strongSelf->_eventEmitter);
227
+ if (emitter)
228
+ emitter->onSelectionChange(
229
+ ENRMSelectionChangeEventFromPayload<EnrichedMarkdownEventEmitter::OnSelectionChange>(payload));
230
+ }];
231
+ #if !TARGET_OS_OSX
232
+ // A handle drag starting or ending is reported right away, even when the
233
+ // selection itself is unchanged.
234
+ _selectionGestures = [[ENRMSelectionGestureTracker alloc] initWithChange:^{
235
+ EnrichedMarkdown *strongSelf = weakSelf;
236
+ if (!strongSelf)
237
+ return;
238
+ UITextView *owner = strongSelf->_selectionOwner;
239
+ if (owner)
240
+ [strongSelf->_selectionGestures trackTextView:owner];
241
+ [strongSelf->_selectionReporter forgetLastReported];
242
+ [strongSelf->_selectionReporter flushNow];
243
+ }];
244
+ #endif
202
245
  }
203
246
  return self;
204
247
  }
205
248
 
249
+ #pragma mark - Selection
250
+
251
+ /// The current selection (or active block) as an `onSelectionChange` payload.
252
+ /// Markdown and `whole` follow the same rules as the intercepted Copy.
253
+ - (NSDictionary *)selectionPayload
254
+ {
255
+ RCTUIView *blockView = _activeBlockView;
256
+ if (blockView != nil) {
257
+ if (blockView.superview != self)
258
+ return ENRMInactiveSelectionPayload();
259
+ return ENRMBlockSelectionPayload(_activeBlockSource, _activeBlockMarkdown, _activeBlockText, blockView.frame);
260
+ }
261
+
262
+ #if !TARGET_OS_OSX
263
+ UITextView *owner = _selectionOwner;
264
+ if (owner == nil || ![owner isDescendantOfView:self])
265
+ return ENRMInactiveSelectionPayload();
266
+ NSDictionary *selection = ENRMTextSelectionInTextView(owner, self);
267
+ if (!selection)
268
+ return ENRMInactiveSelectionPayload();
269
+
270
+ NSString *text = selection[ENRMSelectionTextKey];
271
+ // Segments only see their own text, so a selection is the whole component
272
+ // only when it spans the sole segment; the markdown is then the original.
273
+ BOOL whole = [selection[ENRMSelectionFullKey] boolValue] && _segmentViews.count == 1 && _cachedMarkdown.length > 0;
274
+ NSString *markdown =
275
+ whole ? _cachedMarkdown : extractMarkdownFromAttributedString(owner.attributedText, owner.selectedRange);
276
+ NSMutableDictionary *payload = [selection mutableCopy];
277
+ [payload removeObjectForKey:ENRMSelectionFullKey];
278
+ payload[ENRMSelectionActiveKey] = @YES;
279
+ payload[ENRMSelectionSourceKey] = @"selection";
280
+ payload[ENRMSelectionMarkdownKey] = markdown.length > 0 ? markdown : text;
281
+ payload[ENRMSelectionWholeKey] = @(whole);
282
+ payload[ENRMSelectionAdjustingKey] = @(_selectionGestures.adjusting);
283
+ return payload;
284
+ #else
285
+ return ENRMInactiveSelectionPayload();
286
+ #endif
287
+ }
288
+
289
+ - (void)clearActiveBlock
290
+ {
291
+ _activeBlockView = nil;
292
+ _activeBlockSource = nil;
293
+ _activeBlockMarkdown = nil;
294
+ _activeBlockText = nil;
295
+ }
296
+
297
+ - (void)clearSegmentSelection:(ENRMPlatformTextView *)textView
298
+ {
299
+ #if !TARGET_OS_OSX
300
+ textView.selectedRange = NSMakeRange(0, 0);
301
+ [textView resignFirstResponder];
302
+ #endif
303
+ }
304
+
305
+ - (void)clearSelection
306
+ {
307
+ ENRMPlatformTextView *owner = _selectionOwner;
308
+ _selectionOwner = nil;
309
+ [self clearActiveBlock];
310
+ if (owner)
311
+ [self clearSegmentSelection:owner];
312
+ [_selectionReporter flushNow];
313
+ }
314
+
315
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
316
+ {
317
+ RCTEnrichedMarkdownHandleCommand(self, commandName, args);
318
+ }
319
+
320
+ - (ENRMBlockCopyRequestBlock)blockMenuSuppressedForSource:(NSString *)source view:(RCTUIView *)view
321
+ {
322
+ __weak EnrichedMarkdown *weakSelf = self;
323
+ __weak RCTUIView *weakView = view;
324
+ return ^(NSString *markdown, NSString *text) {
325
+ EnrichedMarkdown *strongSelf = weakSelf;
326
+ RCTUIView *strongView = weakView;
327
+ if (!strongSelf || !strongView || !strongSelf->_selectionReporter.enabled)
328
+ return;
329
+ ENRMPlatformTextView *owner = strongSelf->_selectionOwner;
330
+ strongSelf->_selectionOwner = nil;
331
+ if (owner)
332
+ [strongSelf clearSegmentSelection:owner];
333
+ strongSelf->_activeBlockView = strongView;
334
+ strongSelf->_activeBlockSource = source;
335
+ strongSelf->_activeBlockMarkdown = markdown;
336
+ strongSelf->_activeBlockText = text;
337
+ [strongSelf->_selectionReporter forgetLastReported];
338
+ [strongSelf->_selectionReporter flushNow];
339
+ };
340
+ }
341
+
206
342
  - (void)configureSegmentViewRegistry
207
343
  {
208
344
  __weak EnrichedMarkdown *weakSelf = self;
@@ -273,6 +409,29 @@ static char kENRMSegmentFadeAnimatorKey;
273
409
  }]];
274
410
  #endif
275
411
 
412
+ [handlers addObject:[ENRMSegmentViewHandler handlerWithKind:ENRMSegmentKindCodeBlock
413
+ matchesView:^BOOL(RCTUIView *view, ENRMRenderedSegment *segment) {
414
+ return [view isKindOfClass:[ENRMCodeBlockContainerView class]];
415
+ }
416
+ createView:^RCTUIView *(ENRMRenderedSegment *segment) {
417
+ EnrichedMarkdown *strongSelf = weakSelf;
418
+ if (!strongSelf) {
419
+ return [[RCTUIView alloc] init];
420
+ }
421
+
422
+ ENRMCodeBlockContainerView *view =
423
+ [strongSelf createCodeBlockViewForSegment:segment.codeBlockSegment];
424
+ [strongSelf animateBlockViewIfNeeded:view];
425
+ return view;
426
+ }
427
+ updateView:^(RCTUIView *view, ENRMRenderedSegment *segment) {
428
+ EnrichedMarkdown *strongSelf = weakSelf;
429
+ ENRMCodeBlockContainerView *codeBlockView = (ENRMCodeBlockContainerView *)view;
430
+ codeBlockView.pending =
431
+ strongSelf && segment.codeBlockSegment == strongSelf->_pendingCodeBlockSegment;
432
+ [codeBlockView applyCodeBlockNode:segment.codeBlockSegment.codeBlockNode];
433
+ }]];
434
+
276
435
  _segmentViewRegistry = [[ENRMSegmentViewRegistry alloc] initWithHandlers:handlers];
277
436
  }
278
437
 
@@ -281,6 +440,29 @@ static char kENRMSegmentFadeAnimatorKey;
281
440
  if (_segmentViews.count == 0)
282
441
  return CGSizeZero;
283
442
 
443
+ if (applyFrames) {
444
+ CGFloat overhang = MAX(_config.tableHorizontalOverflow, 0);
445
+ BOOL needsOverhang = NO;
446
+ if (overhang > 0) {
447
+ for (RCTUIView *seg in _segmentViews) {
448
+ if ([seg isKindOfClass:[TableContainerView class]]) {
449
+ needsOverhang = YES;
450
+ break;
451
+ }
452
+ }
453
+ }
454
+ #if TARGET_OS_OSX
455
+ BOOL isClipping = self.layer.masksToBounds;
456
+ if (isClipping && needsOverhang)
457
+ self.layer.masksToBounds = NO;
458
+ else if (!isClipping && !needsOverhang)
459
+ self.layer.masksToBounds = YES;
460
+ #else
461
+ if (self.clipsToBounds != !needsOverhang)
462
+ self.clipsToBounds = !needsOverhang;
463
+ #endif
464
+ }
465
+
284
466
  __block CGFloat yOffset = 0.0;
285
467
  __block CGFloat maxContentWidth = 0.0;
286
468
  const NSUInteger lastIndex = _segmentViews.count - 1;
@@ -290,6 +472,7 @@ static char kENRMSegmentFadeAnimatorKey;
290
472
  const BOOL shouldAddBottomMargin = (!isLast || _allowTrailingMargin);
291
473
 
292
474
  CGFloat segmentHeight = 0;
475
+ const BOOL isTable = [segment isKindOfClass:[TableContainerView class]];
293
476
 
294
477
  if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
295
478
  EnrichedMarkdownInternalText *textView = (EnrichedMarkdownInternalText *)segment;
@@ -298,7 +481,7 @@ static char kENRMSegmentFadeAnimatorKey;
298
481
  segmentHeight = textSize.height;
299
482
  maxContentWidth = MAX(maxContentWidth, textSize.width);
300
483
 
301
- } else if ([segment isKindOfClass:[TableContainerView class]]) {
484
+ } else if (isTable) {
302
485
  yOffset += _config.tableMarginTop;
303
486
  segmentHeight = [(TableContainerView *)segment measureHeight:width];
304
487
  maxContentWidth = width;
@@ -310,9 +493,23 @@ static char kENRMSegmentFadeAnimatorKey;
310
493
  maxContentWidth = width;
311
494
  }
312
495
  #endif
496
+ else if ([segment isKindOfClass:[ENRMCodeBlockContainerView class]]) {
497
+ yOffset += _config.codeBlockMarginTop;
498
+ segmentHeight = [(ENRMCodeBlockContainerView *)segment measureHeight:width];
499
+ maxContentWidth = width;
500
+ }
313
501
 
314
502
  if (applyFrames) {
315
- CGRect segmentFrame = CGRectMake(0, yOffset, width, segmentHeight);
503
+ CGFloat segmentX = 0;
504
+ CGFloat segmentWidth = width;
505
+ if (isTable) {
506
+ CGFloat overhang = MAX(_config.tableHorizontalOverflow, 0);
507
+ if (overhang > 0) {
508
+ segmentX = -overhang;
509
+ segmentWidth = width + overhang * 2;
510
+ }
511
+ }
512
+ CGRect segmentFrame = CGRectMake(segmentX, yOffset, segmentWidth, segmentHeight);
316
513
  segment.frame = segmentFrame;
317
514
  #if TARGET_OS_OSX
318
515
  if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
@@ -335,6 +532,9 @@ static char kENRMSegmentFadeAnimatorKey;
335
532
  yOffset += _config.mathMarginBottom;
336
533
  }
337
534
  #endif
535
+ else if ([segment isKindOfClass:[ENRMCodeBlockContainerView class]] && shouldAddBottomMargin) {
536
+ yOffset += _config.codeBlockMarginBottom;
537
+ }
338
538
  }];
339
539
 
340
540
  return CGSizeMake(maxContentWidth, yOffset);
@@ -353,6 +553,11 @@ static char kENRMSegmentFadeAnimatorKey;
353
553
  return CGSizeMake(measuredWidth, measuredHeight);
354
554
  }
355
555
 
556
+ - (CGSize)lastCommittedLayoutSize
557
+ {
558
+ return _lastCommittedSize.load();
559
+ }
560
+
356
561
  - (BOOL)hasRenderedMarkdown:(NSString *)markdown
357
562
  {
358
563
  return _renderedMarkdown != nil && [_renderedMarkdown isEqualToString:markdown];
@@ -396,6 +601,8 @@ static char kENRMSegmentFadeAnimatorKey;
396
601
  {
397
602
  [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
398
603
 
604
+ _lastCommittedSize.store(CGSizeMake(layoutMetrics.frame.size.width, layoutMetrics.frame.size.height));
605
+
399
606
  NSWritingDirection resolved = _resolvedLayoutDirection;
400
607
  if (layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) {
401
608
  resolved = NSWritingDirectionRightToLeft;
@@ -440,9 +647,30 @@ static char kENRMSegmentFadeAnimatorKey;
440
647
  }
441
648
  }
442
649
 
443
- // Table and math views cache the copy labels at creation time, so re-push them
444
- // on prop updates (e.g. a language change without a remount) to avoid stale
445
- // labels. Only the copy/copy-as-markdown labels apply to these block menus.
650
+ - (ENRMBlockMenuOptions)blockMenuOptions
651
+ {
652
+ return (ENRMBlockMenuOptions){
653
+ .copyHidden = !_selectionMenuConfig.copyEnabled,
654
+ .copyWritesMarkdown = _selectionMenuConfig.copyFormatMarkdown,
655
+ .copyAsMarkdownHidden = !_selectionMenuConfig.copyAsMarkdown,
656
+ .copyButtonHidden = !_selectionMenuConfig.codeBlockCopyButton,
657
+ .interceptCopy = _selectionMenuConfig.interceptCopy,
658
+ };
659
+ }
660
+
661
+ - (ENRMBlockCopyRequestBlock)blockCopyRequestForSource:(NSString *)source
662
+ {
663
+ __weak EnrichedMarkdown *weakSelf = self;
664
+ return ^(NSString *markdown, NSString *text) {
665
+ EnrichedMarkdown *strongSelf = weakSelf;
666
+ if (strongSelf)
667
+ [strongSelf emitCopyRequest:source markdown:markdown text:text whole:YES];
668
+ };
669
+ }
670
+
671
+ // Table, math and code block views cache the copy labels and menu options at
672
+ // creation time, so re-push them on prop updates (e.g. a language change
673
+ // without a remount) to avoid stale values.
446
674
  - (void)pushSelectionMenuLabelsToSegments
447
675
  {
448
676
  for (RCTUIView *segment in _segmentViews) {
@@ -450,14 +678,25 @@ static char kENRMSegmentFadeAnimatorKey;
450
678
  TableContainerView *tableView = (TableContainerView *)segment;
451
679
  tableView.copyLabel = _selectionMenuLabels.copyLabel;
452
680
  tableView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
681
+ tableView.contextMenuHidden = _contextMenuHidden;
682
+ tableView.menuOptions = [self blockMenuOptions];
453
683
  }
454
684
  #if ENRICHED_MARKDOWN_MATH
455
685
  else if ([segment isKindOfClass:[ENRMMathContainerView class]]) {
456
686
  ENRMMathContainerView *mathView = (ENRMMathContainerView *)segment;
457
687
  mathView.copyLabel = _selectionMenuLabels.copyLabel;
458
688
  mathView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
689
+ mathView.contextMenuHidden = _contextMenuHidden;
690
+ mathView.menuOptions = [self blockMenuOptions];
459
691
  }
460
692
  #endif
693
+ else if ([segment isKindOfClass:[ENRMCodeBlockContainerView class]]) {
694
+ ENRMCodeBlockContainerView *codeBlockView = (ENRMCodeBlockContainerView *)segment;
695
+ codeBlockView.copyLabel = _selectionMenuLabels.copyLabel;
696
+ codeBlockView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
697
+ codeBlockView.contextMenuHidden = _contextMenuHidden;
698
+ codeBlockView.menuOptions = [self blockMenuOptions];
699
+ }
461
700
  }
462
701
  }
463
702
 
@@ -466,6 +705,25 @@ static char kENRMSegmentFadeAnimatorKey;
466
705
  ENRMRequestHeightUpdate<EnrichedMarkdownState>(_state, _heightUpdateCounter, self);
467
706
  }
468
707
 
708
+ // A block image in one of the text segments resolved its box height after
709
+ // loading (maxHeight/aspectRatio sizing). The shadow node measured — and
710
+ // cached — this markdown with the pre-load fallback height, so drop those
711
+ // entries, re-stack segments, and re-measure.
712
+ - (void)imageAttachmentDidResolveLayout
713
+ {
714
+ if (_renderedMarkdown.length > 0) {
715
+ MeasurementCache::shared().removeMatchingMarkdown(std::string(_renderedMarkdown.UTF8String));
716
+ }
717
+
718
+ if (self.bounds.size.width > 0) {
719
+ [self setNeedsLayout];
720
+ CGSize measured = [self measureSize:self.bounds.size.width];
721
+ if (needsHeightUpdate(measured, self.bounds)) {
722
+ [self requestHeightUpdate];
723
+ }
724
+ }
725
+ }
726
+
469
727
  - (void)renderMarkdownContent:(NSString *)markdownString
470
728
  {
471
729
  if (_renderCoordinator.blockAsyncRender)
@@ -482,17 +740,21 @@ static char kENRMSegmentFadeAnimatorKey;
482
740
  BOOL allowTrailingMargin = _allowTrailingMargin;
483
741
  BOOL streamingAnimation = _streamingAnimation;
484
742
  ENRMTableStreamingMode tableStreamingMode = _tableStreamingMode;
743
+ ENRMCodeBlockStreamingMode codeBlockStreamingMode = _codeBlockStreamingMode;
485
744
  NSLineBreakStrategy lineBreakStrategy = _lineBreakStrategy;
486
745
  ENRMWritingDirectionMode writingDirectionMode = _writingDirectionMode;
487
746
  NSWritingDirection resolvedLayoutDirection = _resolvedLayoutDirection;
488
747
 
489
748
  __block NSArray<ENRMRenderedSegment *> *renderedSegments = nil;
490
749
  __block NSString *renderableMarkdown = nil;
750
+ __block BOOL endsInsideOpenCodeFence = NO;
491
751
 
492
752
  [_renderCoordinator
493
753
  scheduleRender:^BOOL {
494
- renderableMarkdown = streamingAnimation ? ENRMRenderableMarkdownForStreaming(markdownString, tableStreamingMode)
495
- : markdownString;
754
+ renderableMarkdown = streamingAnimation
755
+ ? ENRMRenderableMarkdownForStreaming(markdownString, tableStreamingMode,
756
+ codeBlockStreamingMode, &endsInsideOpenCodeFence)
757
+ : markdownString;
496
758
 
497
759
  if (renderableMarkdown.length == 0) {
498
760
  renderedSegments = @[];
@@ -515,7 +777,9 @@ static char kENRMSegmentFadeAnimatorKey;
515
777
  }
516
778
  apply:^{
517
779
  self->_renderedStyleFingerprint = self->_pendingStyleFingerprint;
518
- [self applyRenderedSegments:renderedSegments renderedMarkdown:renderableMarkdown];
780
+ [self applyRenderedSegments:renderedSegments
781
+ renderedMarkdown:renderableMarkdown
782
+ endsInsideOpenCodeFence:endsInsideOpenCodeFence];
519
783
  }];
520
784
  }
521
785
 
@@ -552,8 +816,11 @@ static char kENRMSegmentFadeAnimatorKey;
552
816
 
553
817
  _renderCoordinator.blockAsyncRender = YES;
554
818
  _cachedMarkdown = [markdownString copy];
819
+ BOOL endsInsideOpenCodeFence = NO;
555
820
  NSString *renderableMarkdown =
556
- _streamingAnimation ? ENRMRenderableMarkdownForStreaming(markdownString, _tableStreamingMode) : markdownString;
821
+ _streamingAnimation ? ENRMRenderableMarkdownForStreaming(markdownString, _tableStreamingMode,
822
+ _codeBlockStreamingMode, &endsInsideOpenCodeFence)
823
+ : markdownString;
557
824
  _renderedMarkdown = [renderableMarkdown copy];
558
825
  _renderedStyleFingerprint = _pendingStyleFingerprint;
559
826
 
@@ -566,6 +833,8 @@ static char kENRMSegmentFadeAnimatorKey;
566
833
  return;
567
834
  }
568
835
 
836
+ [self updatePendingCodeBlockSegment:endsInsideOpenCodeFence segments:renderedSegments];
837
+
569
838
  for (ENRMRenderedSegment *segment in renderedSegments) {
570
839
  RCTUIView *view = [_segmentViewRegistry createViewForSegment:segment];
571
840
  [_segmentViews addObject:view];
@@ -574,9 +843,24 @@ static char kENRMSegmentFadeAnimatorKey;
574
843
  }
575
844
  }
576
845
 
577
- - (void)applyRenderedSegments:(NSArray *)renderedSegments renderedMarkdown:(NSString *)renderedMarkdown
846
+ - (void)updatePendingCodeBlockSegment:(BOOL)endsInsideOpenCodeFence segments:(NSArray<ENRMRenderedSegment *> *)segments
847
+ {
848
+ _pendingCodeBlockSegment = nil;
849
+ if (!_streamingAnimation || !endsInsideOpenCodeFence) {
850
+ return;
851
+ }
852
+ ENRMRenderedSegment *last = segments.lastObject;
853
+ if (last.kind == ENRMSegmentKindCodeBlock) {
854
+ _pendingCodeBlockSegment = last.codeBlockSegment;
855
+ }
856
+ }
857
+
858
+ - (void)applyRenderedSegments:(NSArray *)renderedSegments
859
+ renderedMarkdown:(NSString *)renderedMarkdown
860
+ endsInsideOpenCodeFence:(BOOL)endsInsideOpenCodeFence
578
861
  {
579
862
  _renderedMarkdown = [renderedMarkdown copy];
863
+ [self updatePendingCodeBlockSegment:endsInsideOpenCodeFence segments:renderedSegments];
580
864
  BOOL segmentTopologyChanged = _streamingAnimation && [self renderedSegmentsChangeTopology:renderedSegments];
581
865
 
582
866
  ENRMSegmentReconciliationResult *result = [ENRMSegmentReconciler reconcileCurrentViews:_segmentViews
@@ -600,6 +884,15 @@ static char kENRMSegmentFadeAnimatorKey;
600
884
  _segmentViews = result.views;
601
885
  _segmentSignatures = result.signatures;
602
886
 
887
+ // A just-closed block has unchanged content, so the reconciler reuses it
888
+ // without an update; sync pending here to trigger its deferred highlight.
889
+ [_segmentViews enumerateObjectsUsingBlock:^(RCTUIView *segment, NSUInteger i, BOOL *stop) {
890
+ if ([segment isKindOfClass:[ENRMCodeBlockContainerView class]]) {
891
+ ((ENRMCodeBlockContainerView *)segment).pending =
892
+ self->_pendingCodeBlockSegment != nil && i == self->_segmentViews.count - 1;
893
+ }
894
+ }];
895
+
603
896
  if (self.bounds.size.width > 0) {
604
897
  [self setNeedsLayout];
605
898
 
@@ -640,6 +933,7 @@ static char kENRMSegmentFadeAnimatorKey;
640
933
 
641
934
  #if !TARGET_OS_OSX
642
935
  view.textView.delegate = self;
936
+ [_selectionGestures trackTextView:view.textView];
643
937
  #else
644
938
  __weak EnrichedMarkdown *weakSelf = self;
645
939
  [view setContextMenuProvider:^NSMenu *_Nullable(NSMenu *baseMenu, NSTextView *textView) {
@@ -647,6 +941,9 @@ static char kENRMSegmentFadeAnimatorKey;
647
941
  if (!strongSelf) {
648
942
  return baseMenu;
649
943
  }
944
+ if (strongSelf->_contextMenuHidden) {
945
+ return nil;
946
+ }
650
947
  NSString *segmentMarkdown = extractMarkdownFromAttributedString(textView.textStorage, textView.selectedRange);
651
948
  NSArray<NSMenuItem *> *customItems = ENRMBuildContextMenuItems(
652
949
  strongSelf->_contextMenuItemTexts, strongSelf->_contextMenuItemIcons, textView,
@@ -676,6 +973,10 @@ static char kENRMSegmentFadeAnimatorKey;
676
973
  tableView.accessibilityLabels = _accessibilityLabels;
677
974
  tableView.copyLabel = _selectionMenuLabels.copyLabel;
678
975
  tableView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
976
+ tableView.contextMenuHidden = _contextMenuHidden;
977
+ tableView.menuOptions = [self blockMenuOptions];
978
+ tableView.onCopyRequest = [self blockCopyRequestForSource:@"table"];
979
+ tableView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"table" view:tableView];
679
980
 
680
981
  __weak EnrichedMarkdown *weakSelf = self;
681
982
 
@@ -715,11 +1016,37 @@ static char kENRMSegmentFadeAnimatorKey;
715
1016
  mathView.accessibilityLabels = _accessibilityLabels;
716
1017
  mathView.copyLabel = _selectionMenuLabels.copyLabel;
717
1018
  mathView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
1019
+ mathView.contextMenuHidden = _contextMenuHidden;
1020
+ mathView.menuOptions = [self blockMenuOptions];
1021
+ mathView.onCopyRequest = [self blockCopyRequestForSource:@"math"];
1022
+ mathView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"math" view:mathView];
718
1023
  [mathView applyLatex:mathSegment.latex];
719
1024
  return mathView;
720
1025
  }
721
1026
  #endif
722
1027
 
1028
+ - (ENRMCodeBlockContainerView *)createCodeBlockViewForSegment:(ENRMCodeBlockSegment *)codeBlockSegment
1029
+ {
1030
+ ENRMCodeBlockContainerView *codeBlockView = [[ENRMCodeBlockContainerView alloc] initWithConfig:_config];
1031
+ codeBlockView.copyLabel = _selectionMenuLabels.copyLabel;
1032
+ codeBlockView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
1033
+ codeBlockView.contextMenuHidden = _contextMenuHidden;
1034
+ codeBlockView.menuOptions = [self blockMenuOptions];
1035
+ codeBlockView.onCopyRequest = [self blockCopyRequestForSource:@"code"];
1036
+ codeBlockView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"code" view:codeBlockView];
1037
+
1038
+ __weak EnrichedMarkdown *weakSelf = self;
1039
+ codeBlockView.onCopyPress = ^(NSString *code, NSString *language) {
1040
+ EnrichedMarkdown *strongSelf = weakSelf;
1041
+ if (strongSelf)
1042
+ [strongSelf emitCopyPress:code language:language];
1043
+ };
1044
+
1045
+ codeBlockView.pending = codeBlockSegment == _pendingCodeBlockSegment;
1046
+ [codeBlockView applyCodeBlockNode:codeBlockSegment.codeBlockNode];
1047
+ return codeBlockView;
1048
+ }
1049
+
723
1050
  - (void)animateBlockViewIfNeeded:(RCTUIView *)view
724
1051
  {
725
1052
  if (!_streamingAnimation)
@@ -759,6 +1086,9 @@ static char kENRMSegmentFadeAnimatorKey;
759
1086
  {
760
1087
  [super layoutSubviews];
761
1088
  [self computeSegmentLayoutForWidth:self.bounds.size.width applyFrames:YES];
1089
+ [self emitLinkLayoutIfNeeded];
1090
+ // Segments may have moved or been replaced; re-read the selection (deduplicated).
1091
+ [_selectionReporter schedule];
762
1092
  }
763
1093
 
764
1094
  - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
@@ -784,8 +1114,24 @@ static char kENRMSegmentFadeAnimatorKey;
784
1114
  }
785
1115
  }
786
1116
 
1117
+ if (ENRMImageRequestHeadersChanged(oldViewProps.imageRequestHeaders, newViewProps.imageRequestHeaders)) {
1118
+ [_config setImageRequestHeaders:ENRMImageRequestHeadersFromProps(newViewProps.imageRequestHeaders)];
1119
+ _dirtyFlags |= ENRMDirtyRender;
1120
+ if (!markdownChanged) {
1121
+ _dirtyFlags |= ENRMDirtyRecreateSegments;
1122
+ }
1123
+ }
1124
+
787
1125
  _selectable = newViewProps.selectable;
788
1126
 
1127
+ if (newViewProps.emitLinkLayout != _emitLinkLayout) {
1128
+ _emitLinkLayout = newViewProps.emitLinkLayout;
1129
+ _lastLinkLayout = nil;
1130
+ [self setNeedsLayout];
1131
+ }
1132
+
1133
+ _selectionReporter.enabled = newViewProps.emitSelectionChange;
1134
+
789
1135
  for (RCTUIView *segment in _segmentViews) {
790
1136
  if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
791
1137
  EnrichedMarkdownInternalText *textSegment = (EnrichedMarkdownInternalText *)segment;
@@ -816,28 +1162,20 @@ static char kENRMSegmentFadeAnimatorKey;
816
1162
  _dirtyFlags |= ENRMDirtyRecreateSegments | ENRMDirtyForceHeight | ENRMDirtyRender;
817
1163
  }
818
1164
 
819
- if (newViewProps.md4cFlags.underline != oldViewProps.md4cFlags.underline) {
820
- _md4cFlags.underline = newViewProps.md4cFlags.underline;
821
- _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
822
- }
823
- if (newViewProps.md4cFlags.superscript != oldViewProps.md4cFlags.superscript) {
824
- _md4cFlags.superscript = newViewProps.md4cFlags.superscript;
825
- _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
826
- }
827
- if (newViewProps.md4cFlags.subscript != oldViewProps.md4cFlags.subscript) {
828
- _md4cFlags.subscript = newViewProps.md4cFlags.subscript;
829
- _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
830
- }
831
- if (newViewProps.md4cFlags.latexMath != oldViewProps.md4cFlags.latexMath) {
832
- _md4cFlags.latexMath = newViewProps.md4cFlags.latexMath;
833
- _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
834
- }
835
- if (newViewProps.md4cFlags.highlight != oldViewProps.md4cFlags.highlight) {
836
- _md4cFlags.highlight = newViewProps.md4cFlags.highlight;
1165
+ if (newViewProps.md4cFlags.underline != oldViewProps.md4cFlags.underline ||
1166
+ newViewProps.md4cFlags.superscript != oldViewProps.md4cFlags.superscript ||
1167
+ newViewProps.md4cFlags.subscript != oldViewProps.md4cFlags.subscript ||
1168
+ newViewProps.md4cFlags.latexMath != oldViewProps.md4cFlags.latexMath ||
1169
+ newViewProps.md4cFlags.highlight != oldViewProps.md4cFlags.highlight ||
1170
+ newViewProps.md4cFlags.hardSoftBreaks != oldViewProps.md4cFlags.hardSoftBreaks) {
1171
+ _md4cFlags = [EnrichedMarkdown flagsFromProps:newViewProps.md4cFlags];
837
1172
  _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
838
1173
  }
839
1174
 
840
1175
  _enableLinkPreview = newViewProps.enableLinkPreview;
1176
+ _linkLongPressSelects = newViewProps.linkLongPressSelects;
1177
+ _enableTaskListItemToggle = newViewProps.enableTaskListItemToggle;
1178
+ _contextMenuHidden = newViewProps.contextMenuHidden;
841
1179
 
842
1180
  if (newViewProps.streamingAnimation != oldViewProps.streamingAnimation) {
843
1181
  _streamingAnimation = newViewProps.streamingAnimation;
@@ -862,15 +1200,21 @@ static char kENRMSegmentFadeAnimatorKey;
862
1200
  _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
863
1201
  }
864
1202
 
1203
+ if (newViewProps.streamingConfig.codeBlockMode != oldViewProps.streamingConfig.codeBlockMode) {
1204
+ NSString *codeBlockModeStr =
1205
+ [[NSString alloc] initWithUTF8String:newViewProps.streamingConfig.codeBlockMode.c_str()];
1206
+ _codeBlockStreamingMode = [codeBlockModeStr isEqualToString:@"hidden"] ? ENRMCodeBlockStreamingModeHidden
1207
+ : ENRMCodeBlockStreamingModeProgressive;
1208
+ _dirtyFlags |= ENRMDirtyForceHeight | ENRMDirtyRender;
1209
+ }
1210
+
865
1211
  if (ENRMContextMenuItemsChanged(oldViewProps.contextMenuItems, newViewProps.contextMenuItems)) {
866
1212
  _contextMenuItemTexts = ENRMContextMenuTextsFromItems(newViewProps.contextMenuItems);
867
1213
  _contextMenuItemIcons = ENRMContextMenuIconsFromItems(newViewProps.contextMenuItems);
868
1214
  }
869
1215
 
870
1216
  _selectionMenuLabels = ENRMParseSelectionMenuLabels(newViewProps.selectionMenuConfig);
871
- _selectionMenuConfig =
872
- ENRMBuildSelectionMenuConfig(_selectionMenuLabels, newViewProps.selectionMenuConfig.copyAsMarkdown,
873
- newViewProps.selectionMenuConfig.copyImageUrl);
1217
+ _selectionMenuConfig = ENRMBuildSelectionMenuConfig(_selectionMenuLabels, newViewProps.selectionMenuConfig);
874
1218
  [self pushSelectionMenuLabelsToSegments];
875
1219
 
876
1220
  if (ENRMAccessibilityLabelsChanged(oldViewProps.accessibilityLabels, newViewProps.accessibilityLabels)) {
@@ -964,7 +1308,8 @@ static char kENRMSegmentFadeAnimatorKey;
964
1308
 
965
1309
  - (void)prepareForRecycle
966
1310
  {
967
- _props = std::make_shared<const EnrichedMarkdownProps>();
1311
+ const auto resetProps = std::make_shared<const EnrichedMarkdownProps>();
1312
+ _props = resetProps;
968
1313
  [_renderCoordinator invalidate];
969
1314
 
970
1315
  for (RCTUIView *segment in _segmentViews) {
@@ -982,8 +1327,39 @@ static char kENRMSegmentFadeAnimatorKey;
982
1327
 
983
1328
  _cachedMarkdown = nil;
984
1329
  _renderedMarkdown = nil;
1330
+ _config = nil;
1331
+ _emitLinkLayout = NO;
1332
+ _lastLinkLayout = nil;
1333
+ _selectionReporter.enabled = NO;
1334
+ #if !TARGET_OS_OSX
1335
+ [_selectionGestures reset];
1336
+ #endif
1337
+ _selectionOwner = nil;
1338
+ [self clearActiveBlock];
1339
+ _md4cFlags = [EnrichedMarkdown flagsFromProps:resetProps->md4cFlags];
1340
+ _maxFontSizeMultiplier = 0;
1341
+ _allowTrailingMargin = NO;
1342
+ _streamingAnimation = NO;
1343
+ _tableStreamingMode = ENRMTableStreamingModeProgressive;
1344
+ _codeBlockStreamingMode = ENRMCodeBlockStreamingModeProgressive;
1345
+ _pendingCodeBlockSegment = nil;
1346
+ _lineBreakStrategy = NSLineBreakStrategyNone;
1347
+ _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
1348
+ _renderedStyleFingerprint = 0;
1349
+ _pendingStyleFingerprint = 0;
1350
+ _contextMenuItemTexts = nil;
1351
+ _contextMenuItemIcons = nil;
1352
+ _fontScaleObserver.allowFontScaling = resetProps->allowFontScaling;
1353
+ _accessibilityLabels = nil;
1354
+ _spoilerOverlay =
1355
+ ENRMSpoilerOverlayFromString([[NSString alloc] initWithUTF8String:resetProps->spoilerOverlay.c_str()]);
1356
+ _dirtyFlags = ENRMDirtyNone;
985
1357
 
986
- [self resetPropDerivedState];
1358
+ // Ported from the fork's resetPropDerivedState: upstream's prepareForRecycle covers every
1359
+ // other prop-derived ivar but leaves these two, so a recycled view keeps the previous
1360
+ // consumer's height-update counter and a dangling shadow-node state handle.
1361
+ _heightUpdateCounter = 0;
1362
+ _state = nullptr;
987
1363
 
988
1364
  [super prepareForRecycle];
989
1365
  }
@@ -1070,6 +1446,40 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
1070
1446
  emitter->onLinkPress({.url = std::string(url.UTF8String)});
1071
1447
  }
1072
1448
 
1449
+ /// Reports link rects of every text segment (offset by the segment origin) to
1450
+ /// JS for `renderLinkDecoration`. Tables are skipped: their cells scroll
1451
+ /// horizontally, so rects would go stale. Runs after each segment layout pass;
1452
+ /// deduplicated so an unchanged layout sends nothing.
1453
+ - (void)emitLinkLayoutIfNeeded
1454
+ {
1455
+ if (!_emitLinkLayout || self.bounds.size.width <= 0)
1456
+ return;
1457
+
1458
+ auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
1459
+ if (!emitter)
1460
+ return;
1461
+
1462
+ NSMutableArray<NSDictionary *> *fragments = [NSMutableArray array];
1463
+ NSInteger linkCount = 0;
1464
+ for (RCTUIView *segment in _segmentViews) {
1465
+ if ([segment isKindOfClass:[TableContainerView class]]) {
1466
+ linkCount += ((TableContainerView *)segment).linkCount;
1467
+ continue;
1468
+ }
1469
+ if (![segment isKindOfClass:[EnrichedMarkdownInternalText class]])
1470
+ continue;
1471
+ NSInteger segmentLinks = 0;
1472
+ [fragments addObjectsFromArray:ENRMCollectLinkLayoutFragments(((EnrichedMarkdownInternalText *)segment).textView,
1473
+ segment.frame.origin, linkCount, &segmentLinks)];
1474
+ linkCount += segmentLinks;
1475
+ }
1476
+
1477
+ if (_lastLinkLayout != nil && [fragments isEqualToArray:_lastLinkLayout])
1478
+ return;
1479
+ _lastLinkLayout = [fragments copy];
1480
+ emitter->onLinkLayout(ENRMLinkLayoutEventFromFragments<EnrichedMarkdownEventEmitter::OnLinkLayout>(fragments));
1481
+ }
1482
+
1073
1483
  - (void)emitLinkLongPress:(NSString *)url
1074
1484
  {
1075
1485
  auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
@@ -1084,6 +1494,26 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
1084
1494
  emitter->onTaskListItemPress({.index = (int)index, .checked = checked, .text = std::string(text.UTF8String ?: "")});
1085
1495
  }
1086
1496
 
1497
+ - (void)emitCopyPress:(NSString *)code language:(NSString *)language
1498
+ {
1499
+ auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
1500
+ if (emitter)
1501
+ emitter->onCopyPress(
1502
+ {.code = std::string(code.UTF8String ?: ""), .language = std::string(language.UTF8String ?: "")});
1503
+ }
1504
+
1505
+ - (void)emitCopyRequest:(NSString *)source markdown:(NSString *)markdown text:(NSString *)text whole:(BOOL)whole
1506
+ {
1507
+ auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
1508
+ if (emitter)
1509
+ emitter->onCopyRequest({
1510
+ .source = std::string(source.UTF8String),
1511
+ .markdown = std::string(markdown.UTF8String ?: ""),
1512
+ .text = std::string(text.UTF8String ?: ""),
1513
+ .whole = (bool)whole,
1514
+ });
1515
+ }
1516
+
1087
1517
  - (void)emitContextMenuItemPress:(NSString *)itemText
1088
1518
  selectedText:(NSString *)selectedText
1089
1519
  selectionStart:(NSUInteger)selectionStart
@@ -1103,7 +1533,8 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
1103
1533
  {
1104
1534
  ENRMPlatformTextView *textView = (ENRMPlatformTextView *)recognizer.view;
1105
1535
 
1106
- if (handleTaskListTapWithSharedLogic(
1536
+ if (_enableTaskListItemToggle &&
1537
+ handleTaskListTapWithSharedLogic(
1107
1538
  textView, recognizer, &self->_cachedMarkdown, self->_config,
1108
1539
  ^(NSInteger index, BOOL checked, NSString *itemText) {
1109
1540
  [self emitTaskListItemPress:index checked:checked text:itemText];
@@ -1126,12 +1557,37 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
1126
1557
  ENRMHandleTapOnTextView(textView, recognizer, ^(NSString *url) { [self emitLinkPress:url]; });
1127
1558
  }
1128
1559
 
1560
+ #if !TARGET_OS_OSX
1561
+ - (void)textViewDidChangeSelection:(UITextView *)textView
1562
+ {
1563
+ [_selectionGestures trackTextView:textView];
1564
+ ENRMDismissSelectionEditMenuIfMoved(textView);
1565
+ if (textView.selectedRange.length > 0) {
1566
+ // Take ownership first so the previous owner's own "cleared" callback is ignored.
1567
+ ENRMPlatformTextView *previous = _selectionOwner;
1568
+ _selectionOwner = textView;
1569
+ [self clearActiveBlock];
1570
+ if (previous && previous != textView)
1571
+ previous.selectedRange = NSMakeRange(0, 0);
1572
+ [_selectionReporter schedule];
1573
+ } else if (textView == _selectionOwner) {
1574
+ [_selectionReporter selectionChanged:NO];
1575
+ }
1576
+ }
1577
+ #endif
1578
+
1129
1579
  // TODO: Remove API_AVAILABLE(ios(16.0)) guard when the minimum iOS deployment target in RN is bumped to 16.
1130
1580
  #if !TARGET_OS_OSX
1131
1581
  - (UIMenu *)textView:(UITextView *)textView
1132
1582
  editMenuForTextInRange:(NSRange)range
1133
1583
  suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
1134
1584
  {
1585
+ // An empty menu presents nothing; nil would make UIKit show its default menu
1586
+ // (the selection and handles stay either way).
1587
+ if (_contextMenuHidden) {
1588
+ return [UIMenu menuWithChildren:@[]];
1589
+ }
1590
+
1135
1591
  __weak EnrichedMarkdown *weakSelf = self;
1136
1592
  ENRMContextMenuPressHandler handler =
1137
1593
  ^(NSString *itemText, NSString *selectedText, NSUInteger selectionStart, NSUInteger selectionEnd) {
@@ -1146,8 +1602,47 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
1146
1602
  ENRMBuildContextMenuActions(_contextMenuItemTexts, _contextMenuItemIcons, textView, range, handler);
1147
1603
 
1148
1604
  NSString *segmentMarkdown = extractMarkdownFromAttributedString(textView.attributedText, range);
1605
+
1606
+ // Segments only see their own text, so a selection is the whole component
1607
+ // only when it spans the sole segment; the markdown is then the original.
1608
+ ENRMCopyRequestHandler onCopyRequest = ^(NSString *markdown, NSString *text, BOOL fullSelection) {
1609
+ EnrichedMarkdown *strongSelf = weakSelf;
1610
+ if (!strongSelf)
1611
+ return;
1612
+ BOOL whole = fullSelection && strongSelf->_segmentViews.count == 1 && strongSelf->_cachedMarkdown.length > 0;
1613
+ [strongSelf emitCopyRequest:@"selection"
1614
+ markdown:whole ? strongSelf->_cachedMarkdown : markdown
1615
+ text:text
1616
+ whole:whole];
1617
+ };
1618
+
1149
1619
  return buildEditMenuForSelection(textView.attributedText, range, segmentMarkdown, _config, suggestedActions,
1150
- customActions, _selectionMenuConfig);
1620
+ customActions, _selectionMenuConfig, onCopyRequest);
1621
+ }
1622
+
1623
+ // Attachments other than images (inline math) are text, not items: without
1624
+ // this a long press on one in an unfocused text view starts UIKit's text item
1625
+ // interaction, which presents nothing and leaves the view unable to select.
1626
+ - (BOOL)textView:(UITextView *)textView
1627
+ shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment
1628
+ inRange:(NSRange)characterRange
1629
+ interaction:(UITextItemInteraction)interaction
1630
+ {
1631
+ if ([textAttachment isKindOfClass:[ENRMImageAttachment class]]) {
1632
+ return YES;
1633
+ }
1634
+ // A long press selects the attachment like a word, as it does once the view is focused.
1635
+ if (interaction == UITextItemInteractionPresentActions) {
1636
+ __weak UITextView *weakTextView = textView;
1637
+ dispatch_async(dispatch_get_main_queue(), ^{
1638
+ UITextView *strongTextView = weakTextView;
1639
+ if (!strongTextView || NSMaxRange(characterRange) > strongTextView.attributedText.length) {
1640
+ return;
1641
+ }
1642
+ ENRMSelectRangeAndPresentEditMenu(strongTextView, characterRange);
1643
+ });
1644
+ }
1645
+ return NO;
1151
1646
  }
1152
1647
 
1153
1648
  - (BOOL)textView:(UITextView *)textView
@@ -1161,6 +1656,18 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
1161
1656
 
1162
1657
  NSString *urlString = linkURLAtRange(textView, characterRange);
1163
1658
 
1659
+ if (urlString && _linkLongPressSelects) {
1660
+ __weak UITextView *weakTextView = textView;
1661
+ dispatch_async(dispatch_get_main_queue(), ^{
1662
+ UITextView *strongTextView = weakTextView;
1663
+ if (!strongTextView || NSMaxRange(characterRange) > strongTextView.attributedText.length) {
1664
+ return;
1665
+ }
1666
+ ENRMSelectRangeAndPresentEditMenu(strongTextView, characterRange);
1667
+ });
1668
+ return NO;
1669
+ }
1670
+
1164
1671
  if (!urlString || _enableLinkPreview) {
1165
1672
  return YES;
1166
1673
  }