react-native-enriched-markdown-scaffold 0.7.4-scaffold.8 → 1.0.2-scaffold.1

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 +191 -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 +27 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +9 -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 +40 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +839 -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 +175 -11
  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 +62 -4
  26. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +911 -460
  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/BlockSyntaxSentinel.kt +69 -0
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +180 -27
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +7 -2
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  65. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  66. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  68. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  69. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  77. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +30 -27
  78. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  79. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  80. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +27 -3
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  90. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  91. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  93. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  94. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  95. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  96. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  97. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  100. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  101. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +33 -7
  102. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  103. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  104. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  105. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  106. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  117. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +19 -2
  118. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +388 -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 +32 -5
  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 +2 -0
  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 +231 -8
  152. package/docs/BREAKING_CHANGES.md +129 -0
  153. package/docs/CODE_HIGHLIGHT.md +133 -0
  154. package/docs/COPY_OPTIONS.md +21 -3
  155. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  156. package/docs/IMAGE_CACHING.md +30 -0
  157. package/docs/INPUT.md +146 -5
  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 +262 -67
  169. package/ios/EnrichedMarkdownText.h +7 -0
  170. package/ios/EnrichedMarkdownText.mm +97 -68
  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 +40 -0
  179. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +839 -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/ENRMBlockSyntaxSentinel.h +25 -0
  190. package/ios/input/ENRMBlockSyntaxSentinel.mm +80 -0
  191. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  192. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  193. package/ios/input/ENRMDetectorPipeline.h +2 -1
  194. package/ios/input/ENRMEditPipeline.h +76 -0
  195. package/ios/input/ENRMEditPipeline.mm +228 -0
  196. package/ios/input/ENRMEditSession.h +36 -0
  197. package/ios/input/ENRMEditSession.mm +63 -0
  198. package/ios/input/ENRMFormattingStore.h +17 -0
  199. package/ios/input/ENRMFormattingStore.mm +95 -105
  200. package/ios/input/ENRMInputBlockType.h +91 -0
  201. package/ios/input/ENRMInputBlockType.mm +5 -0
  202. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  203. package/ios/input/ENRMInputEventEmitter.h +56 -0
  204. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  205. package/ios/input/ENRMInputFormatter.h +29 -69
  206. package/ios/input/ENRMInputFormatter.mm +216 -124
  207. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  208. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  209. package/ios/input/ENRMInputLayoutManager.h +9 -0
  210. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  211. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  212. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  213. package/ios/input/ENRMInputParser.h +10 -6
  214. package/ios/input/ENRMInputParser.mm +527 -171
  215. package/ios/input/ENRMInputRemend.mm +2 -2
  216. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  217. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  218. package/ios/input/ENRMInputTextView.mm +103 -8
  219. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  220. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  221. package/ios/input/ENRMLinkCoordinator.h +41 -0
  222. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  223. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  224. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  225. package/ios/input/ENRMMathAtom.h +26 -0
  226. package/ios/input/ENRMMathAtom.mm +12 -0
  227. package/ios/input/ENRMMentionCoordinator.h +45 -0
  228. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  229. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  230. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  231. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  232. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  233. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  234. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  235. package/ios/input/EnrichedMarkdownTextInput.mm +1104 -1024
  236. package/ios/input/InputStylePropsUtils.h +109 -68
  237. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  238. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  239. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  240. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  241. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  242. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  243. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  244. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  245. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  246. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  247. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  248. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  249. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  250. package/ios/internals/MeasurementCache.h +33 -7
  251. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  252. package/ios/math/ENRMMathChipController.h +74 -0
  253. package/ios/math/ENRMMathChipController.mm +476 -0
  254. package/ios/math/ENRMRaTeXBridge.swift +4 -1
  255. package/ios/parser/ENRMMarkdownParser.h +1 -8
  256. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  257. package/ios/parser/MarkdownASTNode.h +2 -1
  258. package/ios/parser/MarkdownParserBridge.mm +4 -0
  259. package/ios/renderer/AttributedRenderer.m +20 -2
  260. package/ios/renderer/BaseRenderer.h +17 -2
  261. package/ios/renderer/BaseRenderer.m +30 -0
  262. package/ios/renderer/BlockquoteRenderer.h +1 -1
  263. package/ios/renderer/BlockquoteRenderer.m +144 -35
  264. package/ios/renderer/CodeBlockRenderer.h +1 -1
  265. package/ios/renderer/CodeBlockRenderer.m +27 -23
  266. package/ios/renderer/CodeRenderer.h +1 -1
  267. package/ios/renderer/CodeRenderer.m +3 -1
  268. package/ios/renderer/ENRMImageRenderer.h +1 -1
  269. package/ios/renderer/ENRMImageRenderer.m +5 -1
  270. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  271. package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
  272. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  273. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  274. package/ios/renderer/EmphasisRenderer.h +1 -1
  275. package/ios/renderer/EmphasisRenderer.m +3 -1
  276. package/ios/renderer/HeadingRenderer.h +1 -1
  277. package/ios/renderer/HeadingRenderer.m +13 -6
  278. package/ios/renderer/HighlightRenderer.h +1 -1
  279. package/ios/renderer/HighlightRenderer.m +3 -1
  280. package/ios/renderer/LinkRenderer.h +1 -1
  281. package/ios/renderer/LinkRenderer.m +3 -1
  282. package/ios/renderer/ListItemRenderer.h +13 -1
  283. package/ios/renderer/ListItemRenderer.m +102 -44
  284. package/ios/renderer/ListRenderer.h +2 -2
  285. package/ios/renderer/ListRenderer.m +63 -25
  286. package/ios/renderer/ParagraphRenderer.h +1 -1
  287. package/ios/renderer/ParagraphRenderer.m +16 -8
  288. package/ios/renderer/RenderContext.h +10 -0
  289. package/ios/renderer/RenderContext.m +44 -0
  290. package/ios/renderer/RendererFactory.m +14 -0
  291. package/ios/renderer/StrikethroughRenderer.h +1 -1
  292. package/ios/renderer/StrikethroughRenderer.m +3 -1
  293. package/ios/renderer/StrongRenderer.h +1 -1
  294. package/ios/renderer/StrongRenderer.m +3 -1
  295. package/ios/renderer/SubscriptRenderer.h +1 -1
  296. package/ios/renderer/SubscriptRenderer.m +3 -1
  297. package/ios/renderer/SuperscriptRenderer.h +1 -1
  298. package/ios/renderer/SuperscriptRenderer.m +3 -1
  299. package/ios/renderer/TextRenderer.h +2 -2
  300. package/ios/renderer/TextRenderer.m +3 -1
  301. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  302. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  303. package/ios/renderer/UnderlineRenderer.h +1 -1
  304. package/ios/renderer/UnderlineRenderer.m +3 -1
  305. package/ios/styles/StyleConfig.h +22 -0
  306. package/ios/styles/StyleConfig.mm +132 -0
  307. package/ios/utils/BlockquoteBorder.h +6 -0
  308. package/ios/utils/BlockquoteBorder.m +249 -52
  309. package/ios/utils/CodeBlockBackground.m +16 -2
  310. package/ios/utils/ENRMAtomicSize.h +65 -0
  311. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  312. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  313. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  314. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  315. package/ios/utils/ENRMFeatureFlags.h +7 -0
  316. package/ios/utils/ENRMImageDownloader.h +11 -1
  317. package/ios/utils/ENRMImageDownloader.m +60 -16
  318. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  319. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  320. package/ios/utils/ENRMMathFallback.h +26 -0
  321. package/ios/utils/ENRMMathFallback.m +18 -0
  322. package/ios/utils/ENRMTextViewSetup.h +35 -17
  323. package/ios/utils/EditMenuUtils.m +22 -1
  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 +46 -0
  338. package/ios/views/ENRMCodeBlockContainerView.m +566 -0
  339. package/ios/views/ENRMMathContainerView.h +12 -0
  340. package/ios/views/ENRMMathContainerView.m +71 -1
  341. package/ios/views/TableContainerView.h +18 -0
  342. package/ios/views/TableContainerView.m +259 -159
  343. package/jest.d.ts +1 -0
  344. package/jest.js +5 -0
  345. package/lib/module/EnrichedMarkdownNativeComponent.ts +65 -0
  346. package/lib/module/EnrichedMarkdownTextInput.js +86 -5
  347. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  348. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +89 -5
  349. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +66 -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 +30 -4
  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 +62 -0
  383. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  384. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +95 -9
  385. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  386. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +80 -5
  387. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  388. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +62 -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 +65 -1
  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 +65 -0
  433. package/src/EnrichedMarkdownTextInput.tsx +162 -8
  434. package/src/EnrichedMarkdownTextInputNativeComponent.ts +89 -5
  435. package/src/EnrichedMarkdownTextNativeComponent.ts +66 -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 +40 -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 +65 -0
  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;
239
+ _enableTaskListItemToggle = YES;
240
+ _forceHeightUpdateOnNextRender = NO;
241
+ _contextMenuHidden = NO;
234
242
  _selectionMenuConfig = (ENRMSelectionMenuConfig){.copyAsMarkdown = YES, .copyImageURL = YES};
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) {
@@ -495,6 +507,11 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
495
507
  _dirtyFlags |= ENRMDirtyRender;
496
508
  }
497
509
 
510
+ if (ENRMImageRequestHeadersChanged(oldViewProps.imageRequestHeaders, newViewProps.imageRequestHeaders)) {
511
+ [_config setImageRequestHeaders:ENRMImageRequestHeadersFromProps(newViewProps.imageRequestHeaders)];
512
+ _dirtyFlags |= ENRMDirtyRender;
513
+ }
514
+
498
515
  NSLayoutManager *layoutManager = _textView.layoutManager;
499
516
  if ([layoutManager isKindOfClass:[TextViewLayoutManager class]]) {
500
517
  StyleConfig *currentConfig = [layoutManager valueForKey:@"config"];
@@ -539,33 +556,20 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
539
556
  _dirtyFlags |= ENRMDirtyRender;
540
557
  }
541
558
 
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;
559
+ if (newViewProps.md4cFlags.underline != oldViewProps.md4cFlags.underline ||
560
+ newViewProps.md4cFlags.superscript != oldViewProps.md4cFlags.superscript ||
561
+ newViewProps.md4cFlags.subscript != oldViewProps.md4cFlags.subscript ||
562
+ newViewProps.md4cFlags.latexMath != oldViewProps.md4cFlags.latexMath ||
563
+ newViewProps.md4cFlags.highlight != oldViewProps.md4cFlags.highlight ||
564
+ newViewProps.md4cFlags.hardSoftBreaks != oldViewProps.md4cFlags.hardSoftBreaks) {
565
+ _md4cFlags = [EnrichedMarkdownText flagsFromProps:newViewProps.md4cFlags];
564
566
  _forceHeightUpdateOnNextRender = YES;
565
567
  _dirtyFlags |= ENRMDirtyRender;
566
568
  }
567
569
 
568
570
  _enableLinkPreview = newViewProps.enableLinkPreview;
571
+ _enableTaskListItemToggle = newViewProps.enableTaskListItemToggle;
572
+ _contextMenuHidden = newViewProps.contextMenuHidden;
569
573
 
570
574
  if (ENRMContextMenuItemsChanged(oldViewProps.contextMenuItems, newViewProps.contextMenuItems)) {
571
575
  _contextMenuItemTexts = ENRMContextMenuTextsFromItems(newViewProps.contextMenuItems);
@@ -665,26 +669,46 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
665
669
 
666
670
  - (void)prepareForRecycle
667
671
  {
668
- _props = std::make_shared<const EnrichedMarkdownTextProps>();
672
+ const auto resetProps = std::make_shared<const EnrichedMarkdownTextProps>();
673
+ _props = resetProps;
669
674
  [_renderCoordinator invalidate];
670
675
 
671
676
  [_fadeAnimator cancel];
672
677
  _fadeAnimator = nil;
673
678
  _previousTextLength = 0;
679
+ _streamingAnimation = NO;
680
+ _forceHeightUpdateOnNextRender = NO;
674
681
  _cachedMarkdown = nil;
675
682
  _renderedMarkdown = nil;
683
+ _config = nil;
684
+ _md4cFlags = [EnrichedMarkdownText flagsFromProps:resetProps->md4cFlags];
685
+ _maxFontSizeMultiplier = 0;
686
+ _lastElementMarginBottom = 0;
687
+ _allowTrailingMargin = NO;
688
+ _lineBreakStrategy = NSLineBreakStrategyNone;
689
+ _writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
690
+ _renderedStyleFingerprint = 0;
691
+ _pendingStyleFingerprint = 0;
692
+ _contextMenuItemTexts = nil;
693
+ _contextMenuItemIcons = nil;
694
+ _fontScaleObserver.allowFontScaling = resetProps->allowFontScaling;
695
+ _accessibilityLabels = nil;
676
696
  _accessibilityElements = nil;
677
697
  _accessibilityInfo = nil;
678
698
  _accessibilityNeedsRebuild = NO;
679
699
  [_spoilerManager removeAllOverlays];
680
-
681
- [self resetPropDerivedState];
682
-
700
+ _spoilerManager = nil;
683
701
  if (_textView != nil) {
684
702
  ENRMSetAttributedText(_textView, [[NSAttributedString alloc] initWithString:@""]);
685
703
  _textView.hidden = YES;
686
704
  }
687
705
 
706
+ // Ported from the fork's resetPropDerivedState: upstream's prepareForRecycle covers every
707
+ // other prop-derived ivar but leaves these two, so a recycled view keeps the previous
708
+ // consumer's height-update counter and a dangling shadow-node state handle.
709
+ _heightUpdateCounter = 0;
710
+ _state = nullptr;
711
+
688
712
  [super prepareForRecycle];
689
713
  }
690
714
 
@@ -745,7 +769,8 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
745
769
  {
746
770
  ENRMPlatformTextView *textView = (ENRMPlatformTextView *)recognizer.view;
747
771
 
748
- if (handleTaskListTapWithSharedLogic(
772
+ if (_enableTaskListItemToggle &&
773
+ handleTaskListTapWithSharedLogic(
749
774
  textView, recognizer, &self->_cachedMarkdown, self->_config,
750
775
  ^(NSInteger index, BOOL checked, NSString *itemText) {
751
776
  [self emitTaskListItemPress:index checked:checked text:itemText];
@@ -790,6 +815,10 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
790
815
  editMenuForTextInRange:(NSRange)range
791
816
  suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
792
817
  {
818
+ if (_contextMenuHidden) {
819
+ return nil;
820
+ }
821
+
793
822
  __weak EnrichedMarkdownText *weakSelf = self;
794
823
  ENRMContextMenuPressHandler handler =
795
824
  ^(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