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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/README.md +54 -8
  2. package/ReactNativeEnrichedMarkdown.podspec +82 -73
  3. package/android/build.gradle +186 -1
  4. package/android/consumer-rules.pro +1 -1
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +9 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +3 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +24 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +8 -0
  9. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +9 -0
  10. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +3 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +35 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +886 -23
  15. package/android/src/main/baseline-prof.txt +1 -1
  16. package/android/src/main/cpp/jni-adapter.cpp +74 -0
  17. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +189 -13
  18. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +17 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +50 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +48 -1
  21. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +10 -1
  22. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +42 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +82 -4
  24. package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyPressEvent.kt +24 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +54 -4
  26. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +901 -462
  27. package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
  30. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +101 -7
  34. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
  36. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
  39. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  65. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  66. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  68. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  69. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +30 -27
  77. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  78. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  79. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +27 -3
  80. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  90. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  91. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  93. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  94. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  95. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  96. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  97. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  100. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +37 -7
  101. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  102. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  103. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  104. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  105. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  106. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +49 -15
  117. package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +45 -0
  118. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +396 -0
  119. package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
  120. package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
  121. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +36 -9
  122. package/android/src/main/jni/CMakeLists.txt +53 -0
  123. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
  124. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
  125. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
  126. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
  127. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  128. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
  129. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
  130. package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
  131. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +7 -2
  132. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  133. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +6 -4
  134. package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
  135. package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
  136. package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
  137. package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
  138. package/cpp/highlight/HighlightGrammars.hpp +28 -0
  139. package/cpp/highlight/code_highlight_podspec.rb +211 -0
  140. package/cpp/highlight/gen-registry.mjs +155 -0
  141. package/cpp/highlight/grammar-versions.json +159 -0
  142. package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
  143. package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
  144. package/cpp/parser/MD4CParser.cpp +173 -61
  145. package/cpp/parser/MD4CParser.hpp +1 -0
  146. package/cpp/parser/MarkdownASTNode.hpp +2 -1
  147. package/cpp/wasm/ASTSerializer.cpp +2 -0
  148. package/cpp/wasm/build.sh +1 -0
  149. package/cpp/wasm/md4c_wasm.cpp +3 -1
  150. package/docs/ACCESSIBILITY.md +14 -2
  151. package/docs/API_REFERENCE.md +234 -12
  152. package/docs/BREAKING_CHANGES.md +129 -0
  153. package/docs/CODE_HIGHLIGHT.md +133 -0
  154. package/docs/COPY_OPTIONS.md +47 -3
  155. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  156. package/docs/IMAGE_CACHING.md +30 -0
  157. package/docs/INPUT.md +108 -11
  158. package/docs/LATEX_MATH.md +87 -45
  159. package/docs/MACOS.md +1 -1
  160. package/docs/MARKDOWN_STREAMING.md +27 -1
  161. package/docs/NATIVE_ASSETS.md +127 -0
  162. package/docs/RTL.md +1 -1
  163. package/docs/STYLES.md +46 -6
  164. package/docs/TESTING.md +112 -0
  165. package/docs/TEXT.md +16 -6
  166. package/docs/WEB.md +3 -1
  167. package/ios/EnrichedMarkdown.h +7 -0
  168. package/ios/EnrichedMarkdown.mm +284 -75
  169. package/ios/EnrichedMarkdownText.h +7 -0
  170. package/ios/EnrichedMarkdownText.mm +101 -73
  171. package/ios/attachments/ENRMImageAttachment.h +9 -0
  172. package/ios/attachments/ENRMImageAttachment.m +143 -40
  173. package/ios/attachments/ENRMMathInlineAttachment.h +2 -0
  174. package/ios/attachments/ENRMMathInlineAttachment.m +48 -81
  175. package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
  176. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  177. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  178. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +35 -0
  179. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +886 -23
  180. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +131 -0
  181. package/ios/input/ENRMAutoLinkDetector.h +2 -1
  182. package/ios/input/ENRMAutoLinkDetector.mm +79 -41
  183. package/ios/input/ENRMBlockEditCoordinator.h +71 -0
  184. package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
  185. package/ios/input/ENRMBlockRange.h +28 -0
  186. package/ios/input/ENRMBlockRange.mm +30 -0
  187. package/ios/input/ENRMBlockStore.h +56 -0
  188. package/ios/input/ENRMBlockStore.mm +275 -0
  189. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  190. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  191. package/ios/input/ENRMDetectorPipeline.h +2 -1
  192. package/ios/input/ENRMEditPipeline.h +76 -0
  193. package/ios/input/ENRMEditPipeline.mm +228 -0
  194. package/ios/input/ENRMEditSession.h +36 -0
  195. package/ios/input/ENRMEditSession.mm +63 -0
  196. package/ios/input/ENRMFormattingStore.h +17 -0
  197. package/ios/input/ENRMFormattingStore.mm +95 -105
  198. package/ios/input/ENRMInputBlockType.h +91 -0
  199. package/ios/input/ENRMInputBlockType.mm +5 -0
  200. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  201. package/ios/input/ENRMInputEventEmitter.h +56 -0
  202. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  203. package/ios/input/ENRMInputFormatter.h +29 -69
  204. package/ios/input/ENRMInputFormatter.mm +216 -124
  205. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  206. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  207. package/ios/input/ENRMInputLayoutManager.h +9 -0
  208. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  209. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  210. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  211. package/ios/input/ENRMInputParser.h +5 -6
  212. package/ios/input/ENRMInputParser.mm +461 -178
  213. package/ios/input/ENRMInputRemend.mm +2 -2
  214. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  215. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  216. package/ios/input/ENRMInputTextView.mm +103 -8
  217. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  218. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  219. package/ios/input/ENRMLinkCoordinator.h +41 -0
  220. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  221. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  222. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  223. package/ios/input/ENRMMathAtom.h +26 -0
  224. package/ios/input/ENRMMathAtom.mm +12 -0
  225. package/ios/input/ENRMMentionCoordinator.h +45 -0
  226. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  227. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  228. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  229. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  230. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  231. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  232. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  233. package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
  234. package/ios/input/InputStylePropsUtils.h +109 -68
  235. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  236. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  237. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  238. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  239. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  240. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  241. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  242. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  243. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  244. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  245. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  246. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  247. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  248. package/ios/internals/MeasurementCache.h +33 -7
  249. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  250. package/ios/math/ENRMMathChipController.h +74 -0
  251. package/ios/math/ENRMMathChipController.mm +476 -0
  252. package/ios/math/ENRMRaTeXBridge.swift +4 -1
  253. package/ios/parser/ENRMMarkdownParser.h +1 -8
  254. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  255. package/ios/parser/MarkdownASTNode.h +2 -1
  256. package/ios/parser/MarkdownParserBridge.mm +4 -0
  257. package/ios/renderer/AttributedRenderer.m +20 -2
  258. package/ios/renderer/BaseRenderer.h +17 -2
  259. package/ios/renderer/BaseRenderer.m +30 -0
  260. package/ios/renderer/BlockquoteRenderer.h +1 -1
  261. package/ios/renderer/BlockquoteRenderer.m +144 -35
  262. package/ios/renderer/CodeBlockRenderer.h +1 -1
  263. package/ios/renderer/CodeBlockRenderer.m +27 -23
  264. package/ios/renderer/CodeRenderer.h +1 -1
  265. package/ios/renderer/CodeRenderer.m +3 -1
  266. package/ios/renderer/ENRMImageRenderer.h +1 -1
  267. package/ios/renderer/ENRMImageRenderer.m +5 -1
  268. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  269. package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
  270. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  271. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  272. package/ios/renderer/EmphasisRenderer.h +1 -1
  273. package/ios/renderer/EmphasisRenderer.m +3 -1
  274. package/ios/renderer/HeadingRenderer.h +1 -1
  275. package/ios/renderer/HeadingRenderer.m +13 -6
  276. package/ios/renderer/HighlightRenderer.h +1 -1
  277. package/ios/renderer/HighlightRenderer.m +3 -1
  278. package/ios/renderer/LinkRenderer.h +1 -1
  279. package/ios/renderer/LinkRenderer.m +3 -1
  280. package/ios/renderer/ListItemRenderer.h +13 -1
  281. package/ios/renderer/ListItemRenderer.m +102 -44
  282. package/ios/renderer/ListRenderer.h +2 -2
  283. package/ios/renderer/ListRenderer.m +63 -25
  284. package/ios/renderer/ParagraphRenderer.h +1 -1
  285. package/ios/renderer/ParagraphRenderer.m +16 -8
  286. package/ios/renderer/RenderContext.h +10 -0
  287. package/ios/renderer/RenderContext.m +44 -0
  288. package/ios/renderer/RendererFactory.m +14 -0
  289. package/ios/renderer/StrikethroughRenderer.h +1 -1
  290. package/ios/renderer/StrikethroughRenderer.m +3 -1
  291. package/ios/renderer/StrongRenderer.h +1 -1
  292. package/ios/renderer/StrongRenderer.m +3 -1
  293. package/ios/renderer/SubscriptRenderer.h +1 -1
  294. package/ios/renderer/SubscriptRenderer.m +3 -1
  295. package/ios/renderer/SuperscriptRenderer.h +1 -1
  296. package/ios/renderer/SuperscriptRenderer.m +3 -1
  297. package/ios/renderer/TextRenderer.h +2 -2
  298. package/ios/renderer/TextRenderer.m +3 -1
  299. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  300. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  301. package/ios/renderer/UnderlineRenderer.h +1 -1
  302. package/ios/renderer/UnderlineRenderer.m +3 -1
  303. package/ios/styles/StyleConfig.h +22 -0
  304. package/ios/styles/StyleConfig.mm +132 -0
  305. package/ios/utils/BlockquoteBorder.h +6 -0
  306. package/ios/utils/BlockquoteBorder.m +249 -52
  307. package/ios/utils/CodeBlockBackground.m +16 -2
  308. package/ios/utils/ENRMAtomicSize.h +65 -0
  309. package/ios/utils/ENRMBlockMenu.h +57 -0
  310. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  311. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  312. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  313. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  314. package/ios/utils/ENRMFeatureFlags.h +7 -0
  315. package/ios/utils/ENRMImageDownloader.h +11 -1
  316. package/ios/utils/ENRMImageDownloader.m +60 -16
  317. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  318. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  319. package/ios/utils/ENRMMathFallback.h +26 -0
  320. package/ios/utils/ENRMMathFallback.m +18 -0
  321. package/ios/utils/ENRMTextViewSetup.h +35 -17
  322. package/ios/utils/EditMenuUtils.h +22 -5
  323. package/ios/utils/EditMenuUtils.m +53 -9
  324. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  325. package/ios/utils/LastElementUtils.h +1 -0
  326. package/ios/utils/ListMarkerDrawer.m +29 -42
  327. package/ios/utils/MarkdownASTSerializer.m +4 -0
  328. package/ios/utils/ParagraphStyleUtils.h +5 -0
  329. package/ios/utils/ParagraphStyleUtils.m +77 -17
  330. package/ios/utils/RenderedMarkdownSegment.h +13 -1
  331. package/ios/utils/RenderedMarkdownSegment.m +20 -0
  332. package/ios/utils/SegmentRenderer.m +17 -4
  333. package/ios/utils/StreamingMarkdownFilter.h +11 -1
  334. package/ios/utils/StreamingMarkdownFilter.m +98 -2
  335. package/ios/utils/StylePropsUtils.h +63 -0
  336. package/ios/utils/TaskListTapUtils.m +24 -0
  337. package/ios/views/ENRMCodeBlockContainerView.h +53 -0
  338. package/ios/views/ENRMCodeBlockContainerView.m +559 -0
  339. package/ios/views/ENRMMathContainerView.h +16 -0
  340. package/ios/views/ENRMMathContainerView.m +74 -19
  341. package/ios/views/TableContainerView.h +22 -0
  342. package/ios/views/TableContainerView.m +261 -175
  343. package/jest.d.ts +1 -0
  344. package/jest.js +5 -0
  345. package/lib/module/EnrichedMarkdownNativeComponent.ts +73 -0
  346. package/lib/module/EnrichedMarkdownTextInput.js +84 -5
  347. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  348. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  349. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +74 -0
  350. package/lib/module/headingDefaults.js +34 -0
  351. package/lib/module/headingDefaults.js.map +1 -0
  352. package/lib/module/index.js.map +1 -1
  353. package/lib/module/jest/index.js +191 -0
  354. package/lib/module/jest/index.js.map +1 -0
  355. package/lib/module/native/EnrichedMarkdownText.js +37 -5
  356. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  357. package/lib/module/normalizeMarkdownStyle.js +81 -19
  358. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  359. package/lib/module/normalizeMarkdownStyle.web.js +56 -18
  360. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  361. package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
  362. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  363. package/lib/module/normalizeMenuItem.js +1 -1
  364. package/lib/module/utils/textInputState.js +26 -0
  365. package/lib/module/utils/textInputState.js.map +1 -0
  366. package/lib/module/web/EnrichedMarkdownText.js +9 -5
  367. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  368. package/lib/module/web/parseMarkdown.js +4 -3
  369. package/lib/module/web/parseMarkdown.js.map +1 -1
  370. package/lib/module/web/renderers/InlineRenderers.js +6 -0
  371. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  372. package/lib/module/web/renderers/ListRenderers.js +7 -2
  373. package/lib/module/web/renderers/ListRenderers.js.map +1 -1
  374. package/lib/module/web/renderers/index.js +7 -4
  375. package/lib/module/web/renderers/index.js.map +1 -1
  376. package/lib/module/web/styles.js +83 -6
  377. package/lib/module/web/styles.js.map +1 -1
  378. package/lib/module/web/utils.js +1 -0
  379. package/lib/module/web/utils.js.map +1 -1
  380. package/lib/module/web/wasm/md4c.js +0 -0
  381. package/lib/module/web/wasm/md4c.js.map +1 -1
  382. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +66 -0
  383. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  384. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
  385. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  386. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
  387. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  388. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +66 -0
  389. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  390. package/lib/typescript/src/headingDefaults.d.ts +9 -0
  391. package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
  392. package/lib/typescript/src/index.d.ts +2 -2
  393. package/lib/typescript/src/index.d.ts.map +1 -1
  394. package/lib/typescript/src/index.web.d.ts +1 -1
  395. package/lib/typescript/src/index.web.d.ts.map +1 -1
  396. package/lib/typescript/src/jest/index.d.ts +6 -0
  397. package/lib/typescript/src/jest/index.d.ts.map +1 -0
  398. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
  399. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  400. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  401. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  402. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
  403. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  404. package/lib/typescript/src/types/MarkdownStyle.d.ts +72 -0
  405. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  406. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +27 -0
  407. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  408. package/lib/typescript/src/types/MarkdownTextProps.d.ts +104 -9
  409. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  410. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +19 -0
  411. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  412. package/lib/typescript/src/types/events.d.ts +19 -0
  413. package/lib/typescript/src/types/events.d.ts.map +1 -1
  414. package/lib/typescript/src/utils/textInputState.d.ts +24 -0
  415. package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
  416. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  417. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  418. package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
  419. package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
  420. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  421. package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
  422. package/lib/typescript/src/web/renderers/index.d.ts +2 -1
  423. package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
  424. package/lib/typescript/src/web/styles.d.ts +2 -1
  425. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  426. package/lib/typescript/src/web/types.d.ts +6 -2
  427. package/lib/typescript/src/web/types.d.ts.map +1 -1
  428. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  429. package/package.json +36 -9
  430. package/postinstall.mjs +131 -0
  431. package/ratex-version.json +21 -0
  432. package/src/EnrichedMarkdownNativeComponent.ts +73 -0
  433. package/src/EnrichedMarkdownTextInput.tsx +159 -8
  434. package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  435. package/src/EnrichedMarkdownTextNativeComponent.ts +74 -0
  436. package/src/headingDefaults.ts +21 -0
  437. package/src/index.tsx +9 -0
  438. package/src/index.web.tsx +1 -0
  439. package/src/jest/index.tsx +215 -0
  440. package/src/native/EnrichedMarkdownText.tsx +53 -2
  441. package/src/normalizeMarkdownStyle.ts +102 -16
  442. package/src/normalizeMarkdownStyle.web.ts +63 -15
  443. package/src/normalizeMarkdownTextInputStyle.ts +110 -35
  444. package/src/normalizeMenuItem.ts +1 -1
  445. package/src/types/MarkdownStyle.ts +74 -0
  446. package/src/types/MarkdownStyleInternal.ts +39 -0
  447. package/src/types/MarkdownTextProps.ts +101 -9
  448. package/src/types/MarkdownTextProps.web.ts +19 -0
  449. package/src/types/events.ts +21 -0
  450. package/src/utils/textInputState.ts +35 -0
  451. package/src/web/EnrichedMarkdownText.tsx +16 -2
  452. package/src/web/parseMarkdown.ts +6 -2
  453. package/src/web/renderers/InlineRenderers.tsx +5 -0
  454. package/src/web/renderers/ListRenderers.tsx +14 -2
  455. package/src/web/renderers/index.tsx +6 -2
  456. package/src/web/styles.ts +89 -5
  457. package/src/web/types.ts +6 -1
  458. package/src/web/utils.ts +1 -0
  459. package/src/web/wasm/md4c.js +0 -0
  460. package/vendor-grammars.mjs +511 -0
  461. package/vendor-ratex.mjs +173 -0
  462. package/app.plugin.js +0 -2
  463. package/plugin/build/withAndroidMath.js +0 -19
  464. package/plugin/build/withIosMath.js +0 -28
  465. package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
  466. package/plugin/src/withAndroidMath.ts +0 -24
  467. package/plugin/src/withIosMath.ts +0 -35
  468. package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
  469. package/plugin/tsconfig.build.json +0 -16
@@ -3,6 +3,7 @@
3
3
  #import "ContextMenuUtils.h"
4
4
  #import "ENRMAccessibilityLabels.h"
5
5
  #import "ENRMAsyncRenderCoordinator.h"
6
+ #import "ENRMAtomicSize.h"
6
7
  #import "ENRMContextMenuTextView+macOS.h"
7
8
  #import "ENRMImageAttachment.h"
8
9
  #import "ENRMMarkdownParser.h"
@@ -17,6 +18,7 @@
17
18
  #import "FontScaleObserver.h"
18
19
  #import "FontUtils.h"
19
20
  #import "HeightUpdateUtils.h"
21
+ #import "ImageRequestHeaderUtils.h"
20
22
  #import "LinkTapUtils.h"
21
23
  #import "MarkdownASTNode.h"
22
24
  #import "MarkdownAccessibilityElementBuilder.h"
@@ -45,8 +47,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
45
47
  ENRMDirtyRender = 1 << 0,
46
48
  };
47
49
 
48
- @interface EnrichedMarkdownText () <RCTEnrichedMarkdownTextViewProtocol, UITextViewDelegate>
49
- - (void)resetPropDerivedState;
50
+ @interface EnrichedMarkdownText () <RCTEnrichedMarkdownTextViewProtocol, UITextViewDelegate, ENRMImageLayoutObserver>
51
+ + (ENRMMd4cFlags *)flagsFromProps:(const EnrichedMarkdownTextMd4cFlagsStruct &)props;
50
52
  - (void)setupTextView;
51
53
  - (void)renderMarkdownContent:(NSString *)markdownString;
52
54
  - (void)applyRenderedText:(NSMutableAttributedString *)attributedText;
@@ -80,6 +82,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
80
82
  CGFloat _lastElementMarginBottom;
81
83
  BOOL _allowTrailingMargin;
82
84
  BOOL _enableLinkPreview;
85
+ BOOL _enableTaskListItemToggle;
86
+ BOOL _contextMenuHidden;
83
87
  BOOL _streamingAnimation;
84
88
  BOOL _forceHeightUpdateOnNextRender;
85
89
 
@@ -111,6 +115,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
111
115
  NSWritingDirection _resolvedLayoutDirection;
112
116
 
113
117
  ENRMDirtyFlags _dirtyFlags;
118
+
119
+ ENRMAtomicSize _lastCommittedSize;
114
120
  }
115
121
 
116
122
  + (ComponentDescriptorProvider)componentDescriptorProvider
@@ -118,6 +124,18 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
118
124
  return concreteComponentDescriptorProvider<EnrichedMarkdownTextComponentDescriptor>();
119
125
  }
120
126
 
127
+ + (ENRMMd4cFlags *)flagsFromProps:(const EnrichedMarkdownTextMd4cFlagsStruct &)props
128
+ {
129
+ ENRMMd4cFlags *flags = [ENRMMd4cFlags defaultFlags];
130
+ flags.underline = props.underline;
131
+ flags.superscript = props.superscript;
132
+ flags.subscript = props.subscript;
133
+ flags.latexMath = props.latexMath;
134
+ flags.highlight = props.highlight;
135
+ flags.hardSoftBreaks = props.hardSoftBreaks;
136
+ return flags;
137
+ }
138
+
121
139
  #pragma mark - Measuring and State
122
140
 
123
141
  - (CGSize)measureSize:(CGFloat)maxWidth
@@ -130,6 +148,11 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
130
148
  return size;
131
149
  }
132
150
 
151
+ - (CGSize)lastCommittedLayoutSize
152
+ {
153
+ return _lastCommittedSize.load();
154
+ }
155
+
133
156
  - (BOOL)hasRenderedMarkdown:(NSString *)markdown
134
157
  {
135
158
  return _renderedMarkdown != nil && [_renderedMarkdown isEqualToString:markdown];
@@ -158,6 +181,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
158
181
  {
159
182
  [super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
160
183
 
184
+ _lastCommittedSize.store(CGSizeMake(layoutMetrics.frame.size.width, layoutMetrics.frame.size.height));
185
+
161
186
  NSWritingDirection resolved = _resolvedLayoutDirection;
162
187
  if (layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) {
163
188
  resolved = NSWritingDirectionRightToLeft;
@@ -178,44 +203,21 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
178
203
  ENRMRequestHeightUpdate<EnrichedMarkdownTextState>(_state, _heightUpdateCounter, self);
179
204
  }
180
205
 
181
- /// Clears every piece of state that `updateProps` derives from props, back to the
182
- /// values the default-constructed `EnrichedMarkdownTextProps` describes.
183
- ///
184
- /// `updateProps` applies most props as a per-field diff against `_props`, so this
185
- /// state is only correct while it stays in phase with the `_props` baseline it was
186
- /// diffed from. `prepareForRecycle` resets `_props`, so it must reset this too —
187
- /// otherwise every field the next consumer leaves at its default silently inherits
188
- /// the previous consumer's value. Called from `initWithFrame:` as well, so the two
189
- /// baselines cannot drift apart.
190
- ///
191
- /// Requires `_fontScaleObserver` to already exist.
192
- - (void)resetPropDerivedState
193
- {
194
- // Discarded rather than cleared field-by-field: the style diff can only clear a
195
- // field when new and old differ, which never holds against a reset baseline.
196
- _config = nil;
197
- _spoilerManager = nil;
198
-
199
- _md4cFlags = [ENRMMd4cFlags propsDefaultFlags];
200
- _maxFontSizeMultiplier = 0;
201
- _allowTrailingMargin = NO;
202
- _fontScaleObserver.allowFontScaling = YES;
203
- _streamingAnimation = NO;
204
- _lineBreakStrategy = NSLineBreakStrategyNone;
205
- _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
206
- _contextMenuItemTexts = nil;
207
- _contextMenuItemIcons = nil;
208
- _accessibilityLabels = nil;
209
-
210
- // Written only once a render completes, so stale until the next one lands.
211
- _renderedStyleFingerprint = 0;
212
- _pendingStyleFingerprint = 0;
213
- _lastElementMarginBottom = 0;
206
+ // A block image resolved its box height after loading (maxHeight/aspectRatio
207
+ // sizing). The shadow node measured — and cached — this markdown with the
208
+ // pre-load fallback height, so drop those entries and re-measure.
209
+ - (void)imageAttachmentDidResolveLayout
210
+ {
211
+ if (_renderedMarkdown.length > 0) {
212
+ facebook::react::MeasurementCache::shared().removeMatchingMarkdown(std::string(_renderedMarkdown.UTF8String));
213
+ }
214
214
 
215
- _dirtyFlags = ENRMDirtyNone;
216
- _forceHeightUpdateOnNextRender = NO;
217
- _heightUpdateCounter = 0;
218
- _state = nullptr;
215
+ if (self.bounds.size.width > 0) {
216
+ CGSize measured = [self measureSize:self.bounds.size.width];
217
+ if (needsHeightUpdate(measured, self.bounds)) {
218
+ [self requestHeightUpdate];
219
+ }
220
+ }
219
221
  }
220
222
 
221
223
  - (instancetype)initWithFrame:(CGRect)frame
@@ -226,18 +228,25 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
226
228
 
227
229
  self.backgroundColor = [RCTUIColor clearColor];
228
230
  _parser = [[ENRMMarkdownParser alloc] init];
231
+ _md4cFlags = [EnrichedMarkdownText flagsFromProps:defaultProps->md4cFlags];
229
232
 
230
233
  _renderCoordinator =
231
234
  [[ENRMAsyncRenderCoordinator alloc] initWithQueueLabel:"com.swmansion.enriched.markdown.render"];
232
235
 
236
+ _maxFontSizeMultiplier = 0;
237
+ _allowTrailingMargin = NO;
233
238
  _enableLinkPreview = YES;
234
- _selectionMenuConfig = (ENRMSelectionMenuConfig){.copyAsMarkdown = YES, .copyImageURL = YES};
239
+ _enableTaskListItemToggle = YES;
240
+ _forceHeightUpdateOnNextRender = NO;
241
+ _contextMenuHidden = NO;
242
+ _selectionMenuConfig = ENRMDefaultSelectionMenuConfig();
243
+ _lineBreakStrategy = NSLineBreakStrategyNone;
244
+ _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
235
245
  _resolvedLayoutDirection =
236
246
  [[RCTI18nUtil sharedInstance] isRTL] ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight;
247
+ _dirtyFlags = ENRMDirtyNone;
237
248
 
238
249
  _fontScaleObserver = [[FontScaleObserver alloc] init];
239
- [self resetPropDerivedState];
240
-
241
250
  __weak EnrichedMarkdownText *weakSelf = self;
242
251
  _fontScaleObserver.onChange = ^{
243
252
  EnrichedMarkdownText *strongSelf = weakSelf;
@@ -280,6 +289,9 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
280
289
  if (!strongSelf) {
281
290
  return baseMenu;
282
291
  }
292
+ if (strongSelf->_contextMenuHidden) {
293
+ return nil;
294
+ }
283
295
  NSArray<NSMenuItem *> *customItems = ENRMBuildContextMenuItems(
284
296
  strongSelf->_contextMenuItemTexts, strongSelf->_contextMenuItemIcons, textView,
285
297
  ^(NSString *itemText, NSString *selectedText, NSUInteger selectionStart, NSUInteger selectionEnd) {
@@ -289,7 +301,8 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
289
301
  selectionEnd:selectionEnd];
290
302
  });
291
303
  return buildEditMenuForSelection(textView.textStorage, textView.selectedRange, strongSelf->_cachedMarkdown,
292
- strongSelf->_config, @[ baseMenu ], customItems, strongSelf->_selectionMenuConfig);
304
+ strongSelf->_config, @[ baseMenu ], customItems,
305
+ strongSelf -> _selectionMenuConfig);
293
306
  };
294
307
  #endif
295
308
 
@@ -495,6 +508,11 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
495
508
  _dirtyFlags |= ENRMDirtyRender;
496
509
  }
497
510
 
511
+ if (ENRMImageRequestHeadersChanged(oldViewProps.imageRequestHeaders, newViewProps.imageRequestHeaders)) {
512
+ [_config setImageRequestHeaders:ENRMImageRequestHeadersFromProps(newViewProps.imageRequestHeaders)];
513
+ _dirtyFlags |= ENRMDirtyRender;
514
+ }
515
+
498
516
  NSLayoutManager *layoutManager = _textView.layoutManager;
499
517
  if ([layoutManager isKindOfClass:[TextViewLayoutManager class]]) {
500
518
  StyleConfig *currentConfig = [layoutManager valueForKey:@"config"];
@@ -539,33 +557,20 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
539
557
  _dirtyFlags |= ENRMDirtyRender;
540
558
  }
541
559
 
542
- if (newViewProps.md4cFlags.underline != oldViewProps.md4cFlags.underline) {
543
- _md4cFlags.underline = newViewProps.md4cFlags.underline;
544
- _forceHeightUpdateOnNextRender = YES;
545
- _dirtyFlags |= ENRMDirtyRender;
546
- }
547
- if (newViewProps.md4cFlags.superscript != oldViewProps.md4cFlags.superscript) {
548
- _md4cFlags.superscript = newViewProps.md4cFlags.superscript;
549
- _forceHeightUpdateOnNextRender = YES;
550
- _dirtyFlags |= ENRMDirtyRender;
551
- }
552
- if (newViewProps.md4cFlags.subscript != oldViewProps.md4cFlags.subscript) {
553
- _md4cFlags.subscript = newViewProps.md4cFlags.subscript;
554
- _forceHeightUpdateOnNextRender = YES;
555
- _dirtyFlags |= ENRMDirtyRender;
556
- }
557
- if (newViewProps.md4cFlags.latexMath != oldViewProps.md4cFlags.latexMath) {
558
- _md4cFlags.latexMath = newViewProps.md4cFlags.latexMath;
559
- _forceHeightUpdateOnNextRender = YES;
560
- _dirtyFlags |= ENRMDirtyRender;
561
- }
562
- if (newViewProps.md4cFlags.highlight != oldViewProps.md4cFlags.highlight) {
563
- _md4cFlags.highlight = newViewProps.md4cFlags.highlight;
560
+ if (newViewProps.md4cFlags.underline != oldViewProps.md4cFlags.underline ||
561
+ newViewProps.md4cFlags.superscript != oldViewProps.md4cFlags.superscript ||
562
+ newViewProps.md4cFlags.subscript != oldViewProps.md4cFlags.subscript ||
563
+ newViewProps.md4cFlags.latexMath != oldViewProps.md4cFlags.latexMath ||
564
+ newViewProps.md4cFlags.highlight != oldViewProps.md4cFlags.highlight ||
565
+ newViewProps.md4cFlags.hardSoftBreaks != oldViewProps.md4cFlags.hardSoftBreaks) {
566
+ _md4cFlags = [EnrichedMarkdownText flagsFromProps:newViewProps.md4cFlags];
564
567
  _forceHeightUpdateOnNextRender = YES;
565
568
  _dirtyFlags |= ENRMDirtyRender;
566
569
  }
567
570
 
568
571
  _enableLinkPreview = newViewProps.enableLinkPreview;
572
+ _enableTaskListItemToggle = newViewProps.enableTaskListItemToggle;
573
+ _contextMenuHidden = newViewProps.contextMenuHidden;
569
574
 
570
575
  if (ENRMContextMenuItemsChanged(oldViewProps.contextMenuItems, newViewProps.contextMenuItems)) {
571
576
  _contextMenuItemTexts = ENRMContextMenuTextsFromItems(newViewProps.contextMenuItems);
@@ -573,9 +578,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
573
578
  }
574
579
 
575
580
  _selectionMenuLabels = ENRMParseSelectionMenuLabels(newViewProps.selectionMenuConfig);
576
- _selectionMenuConfig =
577
- ENRMBuildSelectionMenuConfig(_selectionMenuLabels, newViewProps.selectionMenuConfig.copyAsMarkdown,
578
- newViewProps.selectionMenuConfig.copyImageUrl);
581
+ _selectionMenuConfig = ENRMBuildSelectionMenuConfig(_selectionMenuLabels, newViewProps.selectionMenuConfig);
579
582
 
580
583
  if (ENRMAccessibilityLabelsChanged(oldViewProps.accessibilityLabels, newViewProps.accessibilityLabels)) {
581
584
  _accessibilityLabels = [[ENRMAccessibilityLabels alloc] init];
@@ -665,26 +668,46 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
665
668
 
666
669
  - (void)prepareForRecycle
667
670
  {
668
- _props = std::make_shared<const EnrichedMarkdownTextProps>();
671
+ const auto resetProps = std::make_shared<const EnrichedMarkdownTextProps>();
672
+ _props = resetProps;
669
673
  [_renderCoordinator invalidate];
670
674
 
671
675
  [_fadeAnimator cancel];
672
676
  _fadeAnimator = nil;
673
677
  _previousTextLength = 0;
678
+ _streamingAnimation = NO;
679
+ _forceHeightUpdateOnNextRender = NO;
674
680
  _cachedMarkdown = nil;
675
681
  _renderedMarkdown = nil;
682
+ _config = nil;
683
+ _md4cFlags = [EnrichedMarkdownText flagsFromProps:resetProps->md4cFlags];
684
+ _maxFontSizeMultiplier = 0;
685
+ _lastElementMarginBottom = 0;
686
+ _allowTrailingMargin = NO;
687
+ _lineBreakStrategy = NSLineBreakStrategyNone;
688
+ _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
689
+ _renderedStyleFingerprint = 0;
690
+ _pendingStyleFingerprint = 0;
691
+ _contextMenuItemTexts = nil;
692
+ _contextMenuItemIcons = nil;
693
+ _fontScaleObserver.allowFontScaling = resetProps->allowFontScaling;
694
+ _accessibilityLabels = nil;
676
695
  _accessibilityElements = nil;
677
696
  _accessibilityInfo = nil;
678
697
  _accessibilityNeedsRebuild = NO;
679
698
  [_spoilerManager removeAllOverlays];
680
-
681
- [self resetPropDerivedState];
682
-
699
+ _spoilerManager = nil;
683
700
  if (_textView != nil) {
684
701
  ENRMSetAttributedText(_textView, [[NSAttributedString alloc] initWithString:@""]);
685
702
  _textView.hidden = YES;
686
703
  }
687
704
 
705
+ // Ported from the fork's resetPropDerivedState: upstream's prepareForRecycle covers every
706
+ // other prop-derived ivar but leaves these two, so a recycled view keeps the previous
707
+ // consumer's height-update counter and a dangling shadow-node state handle.
708
+ _heightUpdateCounter = 0;
709
+ _state = nullptr;
710
+
688
711
  [super prepareForRecycle];
689
712
  }
690
713
 
@@ -745,7 +768,8 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
745
768
  {
746
769
  ENRMPlatformTextView *textView = (ENRMPlatformTextView *)recognizer.view;
747
770
 
748
- if (handleTaskListTapWithSharedLogic(
771
+ if (_enableTaskListItemToggle &&
772
+ handleTaskListTapWithSharedLogic(
749
773
  textView, recognizer, &self->_cachedMarkdown, self->_config,
750
774
  ^(NSInteger index, BOOL checked, NSString *itemText) {
751
775
  [self emitTaskListItemPress:index checked:checked text:itemText];
@@ -790,6 +814,10 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
790
814
  editMenuForTextInRange:(NSRange)range
791
815
  suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
792
816
  {
817
+ if (_contextMenuHidden) {
818
+ return nil;
819
+ }
820
+
793
821
  __weak EnrichedMarkdownText *weakSelf = self;
794
822
  ENRMContextMenuPressHandler handler =
795
823
  ^(NSString *itemText, NSString *selectedText, NSUInteger selectionStart, NSUInteger selectionEnd) {
@@ -5,6 +5,15 @@
5
5
 
6
6
  NS_ASSUME_NONNULL_BEGIN
7
7
 
8
+ /**
9
+ * Adopted by component views that host markdown text. Notified when a block
10
+ * image resolves its box height after loading (maxHeight/aspectRatio sizing),
11
+ * so the view can drop stale cached measurements and request a height update.
12
+ */
13
+ @protocol ENRMImageLayoutObserver
14
+ - (void)imageAttachmentDidResolveLayout;
15
+ @end
16
+
8
17
  /**
9
18
  * Custom NSTextAttachment for rendering markdown images.
10
19
  * Images are loaded asynchronously and scaled dynamically based on text container width.
@@ -5,7 +5,7 @@
5
5
  #import "StyleConfig.h"
6
6
  #import <objc/runtime.h>
7
7
 
8
- #define CACHE_KEY_PROCESSED(url, w, h, r) [NSString stringWithFormat:@"%@_w%.1f_h%.1f_r%.1f", url, w, h, r]
8
+ #define CACHE_KEY_PROCESSED(url, w, h, r, m) [NSString stringWithFormat:@"%@_w%.1f_h%.1f_r%.1f_m%@", url, w, h, r, m]
9
9
 
10
10
  static inline NSUInteger ENRMImageByteCost(RCTUIImage *image)
11
11
  {
@@ -22,13 +22,19 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
22
22
  @interface ENRMImageAttachment ()
23
23
 
24
24
  @property (nonatomic, copy) NSString *imageURL;
25
+ @property (nonatomic, copy, nullable) NSDictionary<NSString *, NSString *> *requestHeaders;
26
+ @property (nonatomic, copy) NSString *cacheKey;
25
27
  @property (nonatomic, assign) BOOL isInline;
26
28
  @property (nonatomic, assign) CGFloat cachedHeight;
29
+ @property (nonatomic, assign) CGFloat cachedMaxHeight;
30
+ @property (nonatomic, assign) CGFloat cachedAspectRatio;
31
+ @property (nonatomic, copy) NSString *cachedResizeMode;
27
32
  @property (nonatomic, assign) CGFloat cachedBorderRadius;
28
33
  @property (nonatomic, weak) NSTextContainer *textContainer;
29
34
  @property (nonatomic, weak) ENRMPlatformTextView *textView;
30
35
  @property (nonatomic, strong) RCTUIImage *originalImage;
31
36
  @property (nonatomic, strong) RCTUIImage *loadedImage;
37
+ @property (nonatomic, strong) RCTUIImage *placeholderImage;
32
38
  @property (nonatomic, copy) NSString *lastProcessedKey;
33
39
 
34
40
  @end
@@ -66,7 +72,8 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
66
72
 
67
73
  + (instancetype)attachmentForURL:(NSString *)imageURL config:(StyleConfig *)config isInline:(BOOL)isInline
68
74
  {
69
- NSString *key = [NSString stringWithFormat:@"%@_%d", imageURL, isInline];
75
+ NSString *key =
76
+ [NSString stringWithFormat:@"%@_%d", ENRMImageCacheKey(imageURL, [config imageRequestHeaders]), isInline];
70
77
  ENRMImageAttachment *existing = [[self attachmentRegistry] objectForKey:key];
71
78
  if (existing && existing.loadedImage) {
72
79
  return existing;
@@ -86,9 +93,14 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
86
93
  self = [super init];
87
94
  if (self) {
88
95
  _imageURL = imageURL;
96
+ _requestHeaders = [[config imageRequestHeaders] copy];
97
+ _cacheKey = ENRMImageCacheKey(imageURL, _requestHeaders);
89
98
  _isInline = isInline;
90
99
 
91
100
  _cachedHeight = isInline ? [config inlineImageSize] : [config imageHeight];
101
+ _cachedMaxHeight = [config imageMaxHeight];
102
+ _cachedAspectRatio = [config imageAspectRatio];
103
+ _cachedResizeMode = [config imageResizeMode];
92
104
  _cachedBorderRadius = [config imageBorderRadius];
93
105
 
94
106
  [self setupPlaceholder];
@@ -97,6 +109,33 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
97
109
  return self;
98
110
  }
99
111
 
112
+ - (CGFloat)resolvedBoxHeightForWidth:(CGFloat)width
113
+ {
114
+ if (self.cachedAspectRatio > 0 && width > 0) {
115
+ return width / self.cachedAspectRatio;
116
+ }
117
+
118
+ if (self.cachedMaxHeight > 0) {
119
+ RCTUIImage *source = self.originalImage;
120
+ if (source && width > 0 && source.size.width > 0 && source.size.height > 0) {
121
+ CGFloat fitted = width * source.size.height / source.size.width;
122
+ return MIN(self.cachedMaxHeight, fitted);
123
+ }
124
+ // Intrinsic size not known yet — fall back to the full max height; the box
125
+ // shrinks once the image loads and layout is invalidated.
126
+ return self.cachedMaxHeight;
127
+ }
128
+
129
+ return self.cachedHeight;
130
+ }
131
+
132
+ // True when resizeMode is unset ('') - legacy always implies a fixed height box.
133
+
134
+ - (BOOL)isLegacyBlockSizing
135
+ {
136
+ return !self.isInline && self.cachedResizeMode.length == 0;
137
+ }
138
+
100
139
  - (CGRect)attachmentBoundsForTextContainer:(NSTextContainer *)textContainer
101
140
  proposedLineFragment:(CGRect)lineFragment
102
141
  glyphPosition:(CGPoint)position
@@ -105,26 +144,27 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
105
144
  CGFloat height = self.cachedHeight;
106
145
  CGFloat width = self.isInline ? height : (lineFragment.size.width > 0 ? lineFragment.size.width : height);
107
146
 
108
- if (self.isInline) {
109
- UIFont *appliedFont = nil;
110
- NSLayoutManager *layoutManager = textContainer.layoutManager;
111
- NSTextStorage *textStorage = layoutManager.textStorage;
147
+ if (!self.isInline) {
148
+ height = [self resolvedBoxHeightForWidth:width];
149
+ return CGRectMake(0, 0, width, height);
150
+ }
112
151
 
113
- if (textStorage && characterIndex < textStorage.length) {
114
- appliedFont = [textStorage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL];
115
- }
152
+ UIFont *appliedFont = nil;
153
+ NSLayoutManager *layoutManager = textContainer.layoutManager;
154
+ NSTextStorage *textStorage = layoutManager.textStorage;
116
155
 
117
- CGFloat verticalOffset;
118
- if (appliedFont) {
119
- verticalOffset = (appliedFont.capHeight - height) / 2.0;
120
- } else {
121
- verticalOffset = (lineFragment.size.height - height) / 2.0;
122
- }
156
+ if (textStorage && characterIndex < textStorage.length) {
157
+ appliedFont = [textStorage attribute:NSFontAttributeName atIndex:characterIndex effectiveRange:NULL];
158
+ }
123
159
 
124
- return CGRectMake(0, verticalOffset, width, height);
160
+ CGFloat verticalOffset;
161
+ if (appliedFont) {
162
+ verticalOffset = (appliedFont.capHeight - height) / 2.0;
163
+ } else {
164
+ verticalOffset = (lineFragment.size.height - height) / 2.0;
125
165
  }
126
166
 
127
- return CGRectMake(0, 0, width, height);
167
+ return CGRectMake(0, verticalOffset, width, height);
128
168
  }
129
169
 
130
170
  - (RCTUIImage *)imageForBounds:(CGRect)imageBounds
@@ -138,7 +178,7 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
138
178
  [self processAndApplyImage:self.originalImage withTargetWidth:imageBounds.size.width];
139
179
  }
140
180
 
141
- return self.loadedImage ?: self.image;
181
+ return self.loadedImage ?: self.placeholderImage;
142
182
  }
143
183
 
144
184
  - (void)handleLoadedImage:(RCTUIImage *)image
@@ -146,7 +186,15 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
146
186
  if (!image)
147
187
  return;
148
188
 
189
+ // The downloader completes synchronously on cache hits, which can happen on the render queue
190
+ if (!NSThread.isMainThread) {
191
+ dispatch_async(dispatch_get_main_queue(), ^{ [self handleLoadedImage:image]; });
192
+ return;
193
+ }
194
+
149
195
  self.originalImage = image;
196
+ // UIKit reads the plain image property for save/drag/copy — it must hold the original
197
+ self.image = image;
150
198
  CGFloat targetWidth = self.isInline ? self.cachedHeight : self.bounds.size.width;
151
199
 
152
200
  // Defer processing if we don't have valid bounds yet (common for non-inline block images)
@@ -162,7 +210,10 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
162
210
  if (targetWidth <= 0)
163
211
  return;
164
212
 
165
- NSString *processedKey = CACHE_KEY_PROCESSED(self.imageURL, targetWidth, self.cachedHeight, self.cachedBorderRadius);
213
+ CGFloat boxHeight = self.isInline ? self.cachedHeight : [self resolvedBoxHeightForWidth:targetWidth];
214
+
215
+ NSString *processedKey =
216
+ CACHE_KEY_PROCESSED(self.cacheKey, targetWidth, boxHeight, self.cachedBorderRadius, self.cachedResizeMode);
166
217
 
167
218
  if ([processedKey isEqualToString:self.lastProcessedKey])
168
219
  return;
@@ -172,8 +223,6 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
172
223
 
173
224
  if (cachedProcessed) {
174
225
  self.loadedImage = cachedProcessed;
175
- if (self.isInline)
176
- self.image = cachedProcessed;
177
226
  [self refreshDisplay];
178
227
  return;
179
228
  }
@@ -186,7 +235,7 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
186
235
 
187
236
  RCTUIImage *processedImage = [strongSelf createScaledImage:image
188
237
  toWidth:targetWidth
189
- height:strongSelf.cachedHeight
238
+ height:boxHeight
190
239
  borderRadius:strongSelf.cachedBorderRadius];
191
240
 
192
241
  if (processedImage) {
@@ -198,10 +247,7 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
198
247
  dispatch_async(dispatch_get_main_queue(), ^{
199
248
  strongSelf.loadedImage = processedImage;
200
249
  if (strongSelf.isInline) {
201
- strongSelf.image = processedImage;
202
250
  strongSelf.bounds = CGRectMake(0, 0, strongSelf.cachedHeight, strongSelf.cachedHeight);
203
- } else {
204
- strongSelf.image = image; // Keep original for layout references
205
251
  }
206
252
  [strongSelf refreshDisplay];
207
253
  });
@@ -218,32 +264,64 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
218
264
  if (sourceWidth <= 0 || sourceHeight <= 0)
219
265
  return nil;
220
266
 
221
- CGFloat drawingWidth, drawingHeight;
267
+ CGSize source = CGSizeMake(sourceWidth, sourceHeight);
268
+ CGSize box = CGSizeMake(targetWidth, targetHeight);
269
+ BOOL legacy = [self isLegacyBlockSizing];
222
270
 
223
- if (!self.isInline) {
224
- CGFloat aspectRatioScale = targetWidth / sourceWidth;
225
- drawingWidth = targetWidth;
226
- drawingHeight = sourceHeight * aspectRatioScale;
271
+ CGRect drawingRect;
272
+ if (self.isInline || legacy) {
273
+ CGFloat drawingWidth, drawingHeight;
274
+ if (!self.isInline) {
275
+ CGFloat aspectRatioScale = targetWidth / sourceWidth;
276
+ drawingWidth = targetWidth;
277
+ drawingHeight = sourceHeight * aspectRatioScale;
278
+ } else {
279
+ drawingWidth = targetWidth;
280
+ drawingHeight = targetHeight;
281
+ }
282
+ drawingRect = CGRectMake((targetWidth - drawingWidth) / 2.0, (targetHeight - drawingHeight) / 2.0, drawingWidth,
283
+ drawingHeight);
227
284
  } else {
228
- drawingWidth = targetWidth;
229
- drawingHeight = targetHeight;
285
+ drawingRect = [self drawingRectForResizeMode:self.cachedResizeMode source:source box:box];
230
286
  }
231
287
 
232
- CGRect drawingRect =
233
- CGRectMake((targetWidth - drawingWidth) / 2.0, (targetHeight - drawingHeight) / 2.0, drawingWidth, drawingHeight);
234
-
235
- RCTUIGraphicsImageRenderer *renderer = ImageRendererForSize(CGSizeMake(targetWidth, targetHeight));
288
+ RCTUIGraphicsImageRenderer *renderer = ImageRendererForSize(box);
236
289
 
237
290
  return [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *context) {
238
291
  if (radius > 0) {
239
- CGRect clippingRect = CGRectIntersection(CGRectMake(0, 0, targetWidth, targetHeight), drawingRect);
240
- UIBezierPath *path = UIBezierPathWithRoundedRect(clippingRect, radius);
292
+ CGRect clipRect = CGRectIntersection(CGRectMake(0, 0, targetWidth, targetHeight), drawingRect);
293
+ UIBezierPath *path = UIBezierPathWithRoundedRect(clipRect, radius);
241
294
  [path addClip];
242
295
  }
243
296
  [image drawInRect:drawingRect];
244
297
  }];
245
298
  }
246
299
 
300
+ - (CGRect)drawingRectForResizeMode:(NSString *)mode source:(CGSize)source box:(CGSize)box
301
+ {
302
+ if ([mode isEqualToString:@"stretch"]) {
303
+ return CGRectMake(0, 0, box.width, box.height);
304
+ }
305
+
306
+ CGFloat widthScale = box.width / source.width;
307
+ CGFloat heightScale = box.height / source.height;
308
+
309
+ CGFloat scale;
310
+ if ([mode isEqualToString:@"contain"]) {
311
+ scale = MIN(widthScale, heightScale);
312
+ } else if ([mode isEqualToString:@"center"]) {
313
+ scale = MIN(1.0, MIN(widthScale, heightScale));
314
+ } else if ([mode isEqualToString:@"none"]) {
315
+ scale = 1.0;
316
+ } else { // cover (default)
317
+ scale = MAX(widthScale, heightScale);
318
+ }
319
+
320
+ CGFloat drawingWidth = source.width * scale;
321
+ CGFloat drawingHeight = source.height * scale;
322
+ return CGRectMake((box.width - drawingWidth) / 2.0, (box.height - drawingHeight) / 2.0, drawingWidth, drawingHeight);
323
+ }
324
+
247
325
  - (void)startDownloadingImage
248
326
  {
249
327
  if (self.imageURL.length == 0)
@@ -251,6 +329,7 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
251
329
 
252
330
  __weak typeof(self) weakSelf = self;
253
331
  [[ENRMImageDownloader shared] downloadURL:self.imageURL
332
+ headers:self.requestHeaders
254
333
  completion:^(RCTUIImage *image) { [weakSelf handleLoadedImage:image]; }];
255
334
  }
256
335
 
@@ -265,10 +344,34 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
265
344
  [textView.layoutManager invalidateDisplayForCharacterRange:range];
266
345
  if (!self.isInline) {
267
346
  [textView.layoutManager invalidateLayoutForCharacterRange:range actualCharacterRange:NULL];
347
+ [self notifyImageLayoutObserver:textView];
268
348
  }
269
349
  }
270
350
  }
271
351
 
352
+ // With maxHeight/aspectRatio sizing the box height can settle after the image
353
+ // loads, while the component was measured (and its size cached) with the
354
+ // pre-load fallback. Notify the hosting component so it can re-measure.
355
+ // Dispatched async: refreshDisplay can run mid-layout (imageForBounds) and
356
+ // re-measuring would re-enter the layout manager.
357
+ - (void)notifyImageLayoutObserver:(UITextView *)textView
358
+ {
359
+ // Only maxHeight/aspectRatio boxes can change height after load; a fixed
360
+ // height box (legacy or explicit resizeMode) never needs a re-measure.
361
+ if (self.cachedMaxHeight <= 0 && self.cachedAspectRatio <= 0)
362
+ return;
363
+
364
+ RCTUIView *candidate = textView;
365
+ while (candidate && ![candidate conformsToProtocol:@protocol(ENRMImageLayoutObserver)]) {
366
+ candidate = candidate.superview;
367
+ }
368
+ if (!candidate)
369
+ return;
370
+
371
+ id<ENRMImageLayoutObserver> observer = (id<ENRMImageLayoutObserver>)candidate;
372
+ dispatch_async(dispatch_get_main_queue(), ^{ [observer imageAttachmentDidResolveLayout]; });
373
+ }
374
+
272
375
  - (ENRMPlatformTextView *)fetchAssociatedTextView
273
376
  {
274
377
  if (self.textView)
@@ -284,7 +387,7 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
284
387
  CGFloat size = self.cachedHeight;
285
388
  self.bounds = CGRectMake(0, 0, size, size);
286
389
  RCTUIGraphicsImageRenderer *renderer = [[RCTUIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(1, 1)];
287
- self.image = [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *ctx){}];
390
+ self.placeholderImage = [renderer imageWithActions:^(RCTUIGraphicsImageRendererContext *ctx){}];
288
391
  }
289
392
 
290
393
  - (NSRange)findAttachmentRangeInText:(NSAttributedString *)attributedString
@@ -302,4 +405,4 @@ static NSMapTable<NSString *, ENRMImageAttachment *> *_attachmentRegistry;
302
405
  return foundRange;
303
406
  }
304
407
 
305
- @end
408
+ @end
@@ -9,6 +9,8 @@ NS_ASSUME_NONNULL_BEGIN
9
9
  @property (nonatomic, assign) CGFloat fontSize;
10
10
  @property (nonatomic, strong, nullable) RCTUIColor *mathTextColor;
11
11
 
12
+ @property (nonatomic, readonly) CGFloat boxHeight;
13
+
12
14
  /// Typeset the glyphs at bold weight (synthetic — see ENRMRaTeXBridge).
13
15
  @property (nonatomic, assign) BOOL bold;
14
16