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
@@ -7,6 +7,7 @@ import android.graphics.Color
7
7
  import android.graphics.Paint
8
8
  import android.graphics.Path
9
9
  import android.graphics.drawable.Drawable
10
+ import android.net.Uri
10
11
  import android.os.Build
11
12
  import android.text.Spannable
12
13
  import android.text.Spanned
@@ -17,9 +18,12 @@ import androidx.core.graphics.createBitmap
17
18
  import androidx.core.graphics.drawable.toDrawable
18
19
  import androidx.core.graphics.withClip
19
20
  import androidx.core.graphics.withSave
21
+ import com.swmansion.enriched.markdown.EnrichedMarkdown
22
+ import com.swmansion.enriched.markdown.EnrichedMarkdownText
20
23
  import com.swmansion.enriched.markdown.styles.StyleConfig
21
24
  import com.swmansion.enriched.markdown.utils.text.ImageCache
22
25
  import com.swmansion.enriched.markdown.utils.text.ImageDownloader
26
+ import com.swmansion.enriched.markdown.utils.text.LocalImageLoader
23
27
  import java.lang.ref.WeakReference
24
28
  import java.util.concurrent.Executors
25
29
  import android.text.style.ImageSpan as AndroidImageSpan
@@ -39,36 +43,84 @@ class ImageSpan(
39
43
  AndroidLineHeightSpan {
40
44
  private var loadedDrawable: Drawable? = null
41
45
  private val height: Int = if (isInline) styleConfig.inlineImageStyle.size.toInt() else styleConfig.imageStyle.height.toInt()
42
- private val borderRadiusPx: Int = (styleConfig.imageStyle.borderRadius * context.resources.displayMetrics.density).toInt()
46
+
47
+ private val borderRadiusPx: Int = styleConfig.imageStyle.borderRadius.toInt()
48
+ private val maxHeightPx: Int = if (isInline) 0 else styleConfig.imageStyle.maxHeight.toInt()
49
+ private val aspectRatio: Float = if (isInline) 0f else styleConfig.imageStyle.aspectRatio
50
+ private val resizeMode: String = if (isInline) "" else styleConfig.imageStyle.resizeMode
51
+
52
+ // so legacy always implies a fixed height box.
53
+ private val legacySizing: Boolean = resizeMode.isEmpty()
54
+
55
+ private val dynamicBoxHeight: Boolean = !isInline && (maxHeightPx > 0 || aspectRatio > 0f)
56
+
57
+ private var boxHeight: Int =
58
+ when {
59
+ isInline -> height
60
+ maxHeightPx > 0 -> maxHeightPx
61
+ else -> height
62
+ }
63
+
64
+ private val requestHeaders: Map<String, String> = styleConfig.imageRequestHeaders
65
+ private val requestKey: String = ImageCache.requestKey(imageUrl, requestHeaders)
43
66
 
44
67
  private var cachedWidth: Int = 0
45
68
  private var viewRef: WeakReference<TextView>? = null
46
69
  private var sourceDrawable: Drawable? = null
47
70
 
71
+ private fun intrinsicImageSize(): Pair<Int, Int> {
72
+ sourceDrawable?.let { return it.intrinsicWidth to it.intrinsicHeight }
73
+ val cached = ImageCache.getOriginal(imageUrl) ?: return 0 to 0
74
+ return cached.width to cached.height
75
+ }
76
+
77
+ // Sizing precedence: aspectRatio > maxHeight > height. Returns the legacy fixed
78
+ // height when no new knob is set.
79
+ private fun resolveBoxHeight(targetWidth: Int): Int {
80
+ if (isInline) return height
81
+ if (aspectRatio > 0f && targetWidth > 0) {
82
+ return (targetWidth / aspectRatio).toInt().coerceAtLeast(1)
83
+ }
84
+ if (maxHeightPx > 0) {
85
+ val (intrinsicWidth, intrinsicHeight) = intrinsicImageSize()
86
+ if (targetWidth > 0 && intrinsicWidth > 0 && intrinsicHeight > 0) {
87
+ val fitted = targetWidth.toFloat() * intrinsicHeight / intrinsicWidth
88
+ return minOf(maxHeightPx.toFloat(), fitted).toInt().coerceAtLeast(1)
89
+ }
90
+ return maxHeightPx
91
+ }
92
+ return height
93
+ }
94
+
95
+ fun prepareForMeasurement(
96
+ text: Spanned,
97
+ widthPx: Int,
98
+ ) {
99
+ if (!dynamicBoxHeight || widthPx <= 0) return
100
+ val available = availableWidthIn(text, widthPx)
101
+ if (available > 0) boxHeight = resolveBoxHeight(available)
102
+ }
103
+
48
104
  init {
49
105
  loadImage()
50
106
  }
51
107
 
52
108
  private fun loadImage() {
53
- if (imageUrl.startsWith("http")) {
54
- ImageDownloader.download(context, imageUrl) { bitmap ->
109
+ val scheme = Uri.parse(imageUrl).scheme?.lowercase()
110
+ if (scheme == "http" || scheme == "https") {
111
+ ImageDownloader.download(context, imageUrl, requestHeaders) { bitmap ->
55
112
  if (bitmap != null) {
56
113
  sourceDrawable = bitmap.toDrawable(context.resources)
57
114
  wrapAndAssignDrawable()
58
115
  }
59
116
  }
60
117
  } else {
61
- val path = imageUrl.removePrefix("file://")
62
- try {
63
- val cached = ImageCache.getOriginal(imageUrl)
64
- val bitmap = cached ?: ImageDownloader.decodeFileDownsampled(context, path)
65
- if (bitmap != null) {
66
- if (cached == null) ImageCache.putOriginal(imageUrl, bitmap)
67
- sourceDrawable = bitmap.toDrawable(context.resources)
68
- wrapAndAssignDrawable()
69
- }
70
- } catch (e: Exception) {
71
- Log.w(TAG, "Failed to load local image: $path", e)
118
+ val cached = ImageCache.getOriginal(imageUrl)
119
+ val bitmap = cached ?: LocalImageLoader.load(context, imageUrl)
120
+ if (bitmap != null) {
121
+ if (cached == null) ImageCache.putOriginal(imageUrl, bitmap)
122
+ sourceDrawable = bitmap.toDrawable(context.resources)
123
+ wrapAndAssignDrawable()
72
124
  }
73
125
  }
74
126
  }
@@ -83,21 +135,26 @@ class ImageSpan(
83
135
  available.coerceAtLeast(0)
84
136
  }
85
137
 
86
- val cachedBitmap = ImageCache.getProcessed(imageUrl, targetWidth, height, borderRadiusPx)
138
+ boxHeight = resolveBoxHeight(targetWidth)
139
+
140
+ val cachedBitmap = ImageCache.getProcessed(requestKey, targetWidth, boxHeight, borderRadiusPx, resizeMode)
141
+
87
142
  if (cachedBitmap != null) {
88
143
  loadedDrawable =
89
144
  cachedBitmap.toDrawable(context.resources).apply {
90
- setBounds(0, 0, targetWidth, height)
145
+ setBounds(0, 0, targetWidth, boxHeight)
91
146
  }
92
147
  } else {
93
148
  loadedDrawable =
94
149
  ScaledImageDrawable(
95
150
  imageDrawable = base,
96
151
  targetWidth = targetWidth,
97
- targetHeight = height,
152
+ targetHeight = boxHeight,
98
153
  borderRadius = borderRadiusPx,
99
154
  isBlockImage = !isInline,
100
- cacheKey = CacheKey(imageUrl, targetWidth, height, borderRadiusPx),
155
+ resizeMode = resizeMode,
156
+ legacySizing = legacySizing,
157
+ cacheKey = CacheKey(requestKey, targetWidth, boxHeight, borderRadiusPx, resizeMode),
101
158
  )
102
159
  }
103
160
  requestReflow()
@@ -116,6 +173,22 @@ class ImageSpan(
116
173
  view.invalidate()
117
174
  view.requestLayout()
118
175
  }
176
+ notifyBoxHeightMayHaveChanged(view)
177
+ }
178
+
179
+ // With maxHeight/aspectRatio the box height can change after the image loads
180
+ // (maxHeight fitted, aspectRatio at a late width). The React container height
181
+ // was measured with the pre-load value, so ask the owning component to
182
+ // re-measure; it only propagates when the stored height actually changed.
183
+ private fun notifyBoxHeightMayHaveChanged(view: TextView) {
184
+ if (!dynamicBoxHeight) return
185
+ if (view is EnrichedMarkdownText) {
186
+ view.layoutManager.invalidateLayout()
187
+ return
188
+ }
189
+ var parent = view.parent
190
+ while (parent != null && parent !is EnrichedMarkdown) parent = parent.parent
191
+ parent?.onImageLayoutChanged()
119
192
  }
120
193
 
121
194
  fun registerTextView(view: TextView) {
@@ -137,12 +210,27 @@ class ImageSpan(
137
210
  cachedWidth = newWidth
138
211
  if (sourceDrawable != null) {
139
212
  wrapAndAssignDrawable()
213
+ return
214
+ }
215
+ if (dynamicBoxHeight) {
216
+ val newBoxHeight = resolveBoxHeight(newWidth)
217
+ if (newBoxHeight != boxHeight) {
218
+ boxHeight = newBoxHeight
219
+ requestReflow()
220
+ }
140
221
  }
141
222
  }
142
223
 
143
224
  private fun getAvailableWidth(view: TextView): Int {
144
225
  val baseWidth = (view.layout?.width ?: view.width).coerceAtLeast(0)
145
226
  val text = view.text as? Spanned ?: return baseWidth
227
+ return availableWidthIn(text, baseWidth)
228
+ }
229
+
230
+ private fun availableWidthIn(
231
+ text: Spanned,
232
+ baseWidth: Int,
233
+ ): Int {
146
234
  val start = text.getSpanStart(this).takeIf { it >= 0 } ?: return baseWidth
147
235
  val end = text.getSpanEnd(this).coerceAtLeast(start)
148
236
  // Subtract leading margins (list bullet/indent, blockquote bar) that consume
@@ -159,7 +247,7 @@ class ImageSpan(
159
247
  val drawable = loadedDrawable ?: transparentDrawable
160
248
  if (drawable !is ScaledImageDrawable) {
161
249
  val drawableWidth = if (isInline) height else cachedWidth.takeIf { it > 0 } ?: drawable.intrinsicWidth
162
- drawable.setBounds(0, 0, drawableWidth.coerceAtLeast(0), height.coerceAtLeast(0))
250
+ drawable.setBounds(0, 0, drawableWidth.coerceAtLeast(0), boxHeight.coerceAtLeast(0))
163
251
  }
164
252
  return drawable
165
253
  }
@@ -182,8 +270,8 @@ class ImageSpan(
182
270
  ) {
183
271
  if (fm == null || isInline) return
184
272
  val currentLineHeight = fm.descent - fm.ascent
185
- if (height > currentLineHeight) {
186
- val extraHeight = height - currentLineHeight
273
+ if (boxHeight > currentLineHeight) {
274
+ val extraHeight = boxHeight - currentLineHeight
187
275
  fm.descent += extraHeight
188
276
  fm.bottom += extraHeight
189
277
  }
@@ -217,6 +305,7 @@ class ImageSpan(
217
305
  val width: Int,
218
306
  val height: Int,
219
307
  val borderRadius: Int,
308
+ val resizeMode: String,
220
309
  )
221
310
 
222
311
  private class ScaledImageDrawable(
@@ -225,6 +314,8 @@ class ImageSpan(
225
314
  private val targetHeight: Int,
226
315
  private val borderRadius: Int,
227
316
  isBlockImage: Boolean,
317
+ private val resizeMode: String = "",
318
+ private val legacySizing: Boolean = true,
228
319
  private val cacheKey: CacheKey? = null,
229
320
  ) : Drawable() {
230
321
  private val clipPath: Path?
@@ -235,14 +326,26 @@ class ImageSpan(
235
326
  val intrinsicWidth = imageDrawable.intrinsicWidth
236
327
  val intrinsicHeight = imageDrawable.intrinsicHeight
237
328
 
329
+ // New sizing needs a clip to the box so cropping
330
+ // modes don't spill over neighbouring lines.
331
+ val clipToBox = isBlockImage && !legacySizing
332
+
238
333
  val (scaledWidth, scaledHeight) =
239
334
  if (intrinsicWidth > 0 && intrinsicHeight > 0) {
240
- if (isBlockImage) {
241
- val scale = targetWidth.toFloat() / intrinsicWidth
242
- targetWidth to (intrinsicHeight * scale).toInt()
243
- } else {
244
- val scale = minOf(targetWidth.toFloat() / intrinsicWidth, targetHeight.toFloat() / intrinsicHeight)
245
- (intrinsicWidth * scale).toInt() to (intrinsicHeight * scale).toInt()
335
+ when {
336
+ !isBlockImage -> {
337
+ val scale = minOf(targetWidth.toFloat() / intrinsicWidth, targetHeight.toFloat() / intrinsicHeight)
338
+ (intrinsicWidth * scale).toInt() to (intrinsicHeight * scale).toInt()
339
+ }
340
+
341
+ legacySizing -> {
342
+ val scale = targetWidth.toFloat() / intrinsicWidth
343
+ targetWidth to (intrinsicHeight * scale).toInt()
344
+ }
345
+
346
+ else -> {
347
+ resizeModeSize(intrinsicWidth, intrinsicHeight)
348
+ }
246
349
  }
247
350
  } else {
248
351
  targetWidth to targetHeight
@@ -252,26 +355,53 @@ class ImageSpan(
252
355
  val top = (targetHeight - scaledHeight) / 2
253
356
  imageDrawable.setBounds(left, top, left + scaledWidth, top + scaledHeight)
254
357
 
358
+ val clipLeft = maxOf(0, left).toFloat()
359
+ val clipTop = maxOf(0, top).toFloat()
360
+ val clipRight = minOf(targetWidth, left + scaledWidth).toFloat()
361
+ val clipBottom = minOf(targetHeight, top + scaledHeight).toFloat()
255
362
  clipPath =
256
- if (borderRadius > 0) {
257
- val clipLeft = maxOf(0, left).toFloat()
258
- val clipTop = maxOf(0, top).toFloat()
259
- val clipRight = minOf(targetWidth, left + scaledWidth).toFloat()
260
- val clipBottom = minOf(targetHeight, top + scaledHeight).toFloat()
261
- Path().apply {
262
- addRoundRect(
263
- clipLeft,
264
- clipTop,
265
- clipRight,
266
- clipBottom,
267
- borderRadius.toFloat(),
268
- borderRadius.toFloat(),
269
- Path.Direction.CW,
270
- )
363
+ when {
364
+ borderRadius > 0 -> {
365
+ Path().apply {
366
+ addRoundRect(
367
+ clipLeft,
368
+ clipTop,
369
+ clipRight,
370
+ clipBottom,
371
+ borderRadius.toFloat(),
372
+ borderRadius.toFloat(),
373
+ Path.Direction.CW,
374
+ )
375
+ }
271
376
  }
272
- } else {
273
- null
377
+
378
+ clipToBox -> {
379
+ Path().apply {
380
+ addRect(clipLeft, clipTop, clipRight, clipBottom, Path.Direction.CW)
381
+ }
382
+ }
383
+
384
+ else -> {
385
+ null
386
+ }
387
+ }
388
+ }
389
+
390
+ private fun resizeModeSize(
391
+ intrinsicWidth: Int,
392
+ intrinsicHeight: Int,
393
+ ): Pair<Int, Int> {
394
+ if (resizeMode == "stretch") return targetWidth to targetHeight
395
+ val widthScale = targetWidth.toFloat() / intrinsicWidth
396
+ val heightScale = targetHeight.toFloat() / intrinsicHeight
397
+ val scale =
398
+ when (resizeMode) {
399
+ "contain" -> minOf(widthScale, heightScale)
400
+ "center" -> minOf(1f, minOf(widthScale, heightScale))
401
+ "none" -> 1f
402
+ else -> maxOf(widthScale, heightScale) // cover (default)
274
403
  }
404
+ return (intrinsicWidth * scale).toInt() to (intrinsicHeight * scale).toInt()
275
405
  }
276
406
 
277
407
  override fun draw(canvas: Canvas) {
@@ -303,7 +433,14 @@ class ImageSpan(
303
433
  } else {
304
434
  source.draw(offscreen)
305
435
  }
306
- ImageCache.putProcessed(cachedKey.url, cachedKey.width, cachedKey.height, cachedKey.borderRadius, bitmap)
436
+ ImageCache.putProcessed(
437
+ cachedKey.url,
438
+ cachedKey.width,
439
+ cachedKey.height,
440
+ cachedKey.borderRadius,
441
+ cachedKey.resizeMode,
442
+ bitmap,
443
+ )
307
444
  } catch (_: OutOfMemoryError) {
308
445
  Log.e("ScaledImageDrawable", "OOM caching bitmap ${width}x$height")
309
446
  }
@@ -328,7 +465,6 @@ class ImageSpan(
328
465
  }
329
466
 
330
467
  companion object {
331
- private const val TAG = "ImageSpan"
332
468
  private val transparentDrawable by lazy { Color.TRANSPARENT.toDrawable() }
333
469
  private val cacheExecutor = Executors.newSingleThreadExecutor()
334
470
  }
@@ -0,0 +1,33 @@
1
+ package com.swmansion.enriched.markdown.spans
2
+
3
+ import android.graphics.Paint
4
+ import android.text.Spanned
5
+ import android.text.style.LineHeightSpan
6
+
7
+ /**
8
+ * Adds vertical spacing between list items using LineHeightSpan.
9
+ *
10
+ * Attached over the newline that separates two items; extends only the line
11
+ * ending at that newline so the gap appears below the preceding item.
12
+ *
13
+ * @param spacing The spacing in pixels to add below the line (0 = no spacing)
14
+ */
15
+ class ListItemSpacingSpan(
16
+ val spacing: Float,
17
+ ) : LineHeightSpan {
18
+ override fun chooseHeight(
19
+ text: CharSequence,
20
+ start: Int,
21
+ end: Int,
22
+ spanstartv: Int,
23
+ lineHeight: Int,
24
+ fm: Paint.FontMetricsInt,
25
+ ) {
26
+ val spanEnd = (text as? Spanned)?.getSpanEnd(this) ?: return
27
+ if (end != spanEnd) return
28
+
29
+ val spacingPixels = spacing.toInt()
30
+ fm.descent += spacingPixels
31
+ fm.bottom += spacingPixels
32
+ }
33
+ }
@@ -0,0 +1,38 @@
1
+ package com.swmansion.enriched.markdown.spans
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.Paint
5
+ import android.text.Layout
6
+ import android.text.Spanned
7
+ import android.text.style.LeadingMarginSpan
8
+
9
+ // Draws a list marker on a line owned by another block (code block, nested
10
+ // sublist) without contributing any margin of its own.
11
+ class ListMarkerAnchorSpan(
12
+ internal val marker: BaseListSpan,
13
+ ) : LeadingMarginSpan {
14
+ override fun getLeadingMargin(first: Boolean): Int = 0
15
+
16
+ override fun drawLeadingMargin(
17
+ c: Canvas,
18
+ p: Paint,
19
+ x: Int,
20
+ dir: Int,
21
+ top: Int,
22
+ baseline: Int,
23
+ bottom: Int,
24
+ text: CharSequence?,
25
+ start: Int,
26
+ end: Int,
27
+ first: Boolean,
28
+ layout: Layout?,
29
+ ) {
30
+ if (!first) return
31
+ if (text is Spanned && text.getSpanStart(this) != start) return
32
+
33
+ // Pass the paragraph origin: drawMarker reconstructs its offset from depth,
34
+ // matching the x its own span would receive on a plain text line.
35
+ val originX = if (dir > 0) 0 else layout?.width ?: 0
36
+ marker.drawMarkerAt(c, p, originX, dir, top, baseline, bottom, layout, start)
37
+ }
38
+ }
@@ -18,6 +18,7 @@ class OrderedListSpan(
18
18
  depth: Int,
19
19
  context: Context,
20
20
  styleCache: SpanStyleCache,
21
+ drawsMarker: Boolean = true,
21
22
  ) : BaseListSpan(
22
23
  depth = depth,
23
24
  context = context,
@@ -31,6 +32,7 @@ class OrderedListSpan(
31
32
  ),
32
33
  marginLeft = listStyle.marginLeft,
33
34
  gapWidth = listStyle.gapWidth,
35
+ drawsMarker = drawsMarker,
34
36
  ) {
35
37
  companion object {
36
38
  private val sharedMarkerPaint = TextPaint().apply { isAntiAlias = true }
@@ -31,7 +31,7 @@ class StrongSpan(
31
31
  tp.typeface =
32
32
  if (styleCache.strongFontFamily.isNotEmpty()) {
33
33
  val resolvedStyle = if (styleCache.strongFontWeight == "normal") Typeface.NORMAL else style
34
- SpanStyleCache.getTypeface(styleCache.strongFontFamily, resolvedStyle)
34
+ styleCache.getTypeface(styleCache.strongFontFamily, resolvedStyle)
35
35
  } else {
36
36
  Typeface.create(currentTypeface, style)
37
37
  }
@@ -19,6 +19,7 @@ class TaskListSpan(
19
19
  styleCache: SpanStyleCache,
20
20
  val taskIndex: Int,
21
21
  val isChecked: Boolean,
22
+ drawsMarker: Boolean = true,
22
23
  ) : BaseListSpan(
23
24
  depth = depth,
24
25
  context = context,
@@ -32,6 +33,7 @@ class TaskListSpan(
32
33
  ),
33
34
  marginLeft = listStyle.marginLeft,
34
35
  gapWidth = listStyle.gapWidth,
36
+ drawsMarker = drawsMarker,
35
37
  ) {
36
38
  private val checkboxSize = taskStyle.checkboxSize
37
39
  private val markerColumnWidth = listStyle.effectiveMarkerWidth(checkboxSize)
@@ -69,8 +71,7 @@ class TaskListSpan(
69
71
  layout: Layout?,
70
72
  start: Int,
71
73
  ) {
72
- val fontMetrics = paint.fontMetrics
73
- val capHeight = -fontMetrics.ascent * CAP_HEIGHT_RATIO
74
+ val capHeight = -markerFontMetrics.ascent * CAP_HEIGHT_RATIO
74
75
  val centerY = baseline - capHeight / HALF_DIVISOR
75
76
  // Right-align the checkbox inside the reserved marker column so it hugs
76
77
  // the gap before the text. At the default (markerColumnWidth ==
@@ -13,6 +13,7 @@ class UnorderedListSpan(
13
13
  depth: Int,
14
14
  context: Context,
15
15
  styleCache: SpanStyleCache,
16
+ drawsMarker: Boolean = true,
16
17
  ) : BaseListSpan(
17
18
  depth = depth,
18
19
  context = context,
@@ -26,6 +27,7 @@ class UnorderedListSpan(
26
27
  ),
27
28
  marginLeft = listStyle.marginLeft,
28
29
  gapWidth = listStyle.gapWidth,
30
+ drawsMarker = drawsMarker,
29
31
  ) {
30
32
  companion object {
31
33
  private val sharedBulletPaint =
@@ -64,8 +66,7 @@ class UnorderedListSpan(
64
66
  // visually flush-left when the column width equals the bullet radius
65
67
  // (the default).
66
68
  val bulletX = x + (depth * marginLeft + markerColumnWidth) * dir
67
- val fontMetrics = paint.fontMetrics
68
- val bulletY = baseline + (fontMetrics.ascent + fontMetrics.descent) / 2f
69
+ val bulletY = baseline + (markerFontMetrics.ascent + markerFontMetrics.descent) / 2f
69
70
 
70
71
  when (depth) {
71
72
  0 -> {
@@ -14,6 +14,8 @@ data class BlockquoteStyle(
14
14
  val borderWidth: Float,
15
15
  val gapWidth: Float,
16
16
  val backgroundColor: Int?,
17
+ val borderRadius: Float,
18
+ val padding: Float,
17
19
  ) : BaseBlockStyle {
18
20
  companion object {
19
21
  fun fromReadableMap(
@@ -32,6 +34,8 @@ data class BlockquoteStyle(
32
34
  val borderWidth = parser.toPixelFromDIP(map.getDouble("borderWidth").toFloat())
33
35
  val gapWidth = parser.toPixelFromDIP(map.getDouble("gapWidth").toFloat())
34
36
  val backgroundColor = parser.parseOptionalColor(map, "backgroundColor")
37
+ val borderRadius = parser.toPixelFromDIP(map.getDouble("borderRadius").toFloat())
38
+ val padding = parser.toPixelFromDIP(map.getDouble("padding").toFloat())
35
39
 
36
40
  return BlockquoteStyle(
37
41
  fontSize,
@@ -45,6 +49,8 @@ data class BlockquoteStyle(
45
49
  borderWidth,
46
50
  gapWidth,
47
51
  backgroundColor,
52
+ borderRadius,
53
+ padding,
48
54
  )
49
55
  }
50
56
  }
@@ -15,8 +15,27 @@ data class CodeBlockStyle(
15
15
  val borderRadius: Float,
16
16
  val borderWidth: Float,
17
17
  val padding: Float,
18
+ val syntaxColors: List<Int>,
18
19
  ) : BaseBlockStyle {
19
20
  companion object {
21
+ private val SYNTAX_COLOR_KEYS =
22
+ listOf(
23
+ "keyword",
24
+ "operatorColor",
25
+ "punctuation",
26
+ "string",
27
+ "number",
28
+ "constant",
29
+ "comment",
30
+ "function",
31
+ "type",
32
+ "variable",
33
+ "property",
34
+ "tag",
35
+ "attribute",
36
+ "embedded",
37
+ )
38
+
20
39
  fun fromReadableMap(
21
40
  map: ReadableMap,
22
41
  parser: StyleParser,
@@ -35,6 +54,12 @@ data class CodeBlockStyle(
35
54
  val borderWidth = parser.toPixelFromDIP(map.getDouble("borderWidth").toFloat())
36
55
  val padding = parser.toPixelFromDIP(map.getDouble("padding").toFloat())
37
56
 
57
+ val syntaxMap = map.getMap("syntaxColors")
58
+ val syntaxColors =
59
+ SYNTAX_COLOR_KEYS.map { key ->
60
+ syntaxMap?.let { parser.parseOptionalColor(it, key) } ?: color
61
+ }
62
+
38
63
  return CodeBlockStyle(
39
64
  fontSize,
40
65
  fontFamily,
@@ -48,6 +73,7 @@ data class CodeBlockStyle(
48
73
  borderRadius,
49
74
  borderWidth,
50
75
  padding,
76
+ syntaxColors,
51
77
  )
52
78
  }
53
79
  }
@@ -4,6 +4,9 @@ import com.facebook.react.bridge.ReadableMap
4
4
 
5
5
  data class ImageStyle(
6
6
  val height: Float,
7
+ val maxHeight: Float,
8
+ val aspectRatio: Float,
9
+ val resizeMode: String,
7
10
  val borderRadius: Float,
8
11
  val marginTop: Float,
9
12
  val marginBottom: Float,
@@ -14,10 +17,13 @@ data class ImageStyle(
14
17
  parser: StyleParser,
15
18
  ): ImageStyle {
16
19
  val height = parser.toPixelFromDIP(map.getDouble("height").toFloat())
20
+ val maxHeight = parser.toPixelFromDIP(map.getDouble("maxHeight").toFloat())
21
+ val aspectRatio = map.getDouble("aspectRatio").toFloat()
22
+ val resizeMode = map.getString("resizeMode") ?: ""
17
23
  val borderRadius = parser.toPixelFromDIP(map.getDouble("borderRadius").toFloat())
18
24
  val marginTop = parser.toPixelFromDIP(map.getDouble("marginTop").toFloat())
19
25
  val marginBottom = parser.toPixelFromDIP(map.getDouble("marginBottom").toFloat())
20
- return ImageStyle(height, borderRadius, marginTop, marginBottom)
26
+ return ImageStyle(height, maxHeight, aspectRatio, resizeMode, borderRadius, marginTop, marginBottom)
21
27
  }
22
28
  }
23
29
  }
@@ -17,6 +17,7 @@ data class ListStyle(
17
17
  val markerFontWeight: String,
18
18
  val gapWidth: Float,
19
19
  val marginLeft: Float,
20
+ val itemSpacing: Float,
20
21
  ) : BaseBlockStyle {
21
22
  fun effectiveMarkerWidth(naturalWidth: Float): Float = naturalWidth.coerceAtLeast(markerMinWidth)
22
23
 
@@ -34,12 +35,13 @@ data class ListStyle(
34
35
  val lineHeightRaw = map.getDouble("lineHeight").toFloat()
35
36
  val lineHeight = parser.toPixelFromSP(lineHeightRaw)
36
37
  val bulletColor = parser.parseColor(map, "bulletColor")
37
- val bulletSize = parser.toPixelFromDIP(map.getDouble("bulletSize").toFloat())
38
+ val bulletSize = parser.toPixelFromSP(map.getDouble("bulletSize").toFloat())
38
39
  val markerMinWidth = parser.toPixelFromDIP(map.getDouble("markerMinWidth").toFloat().coerceAtLeast(0f))
39
40
  val markerColor = parser.parseColor(map, "markerColor")
40
41
  val markerFontWeight = parser.parseString(map, "markerFontWeight", "normal")
41
42
  val gapWidth = parser.toPixelFromDIP(map.getDouble("gapWidth").toFloat())
42
43
  val marginLeft = parser.toPixelFromDIP(map.getDouble("marginLeft").toFloat())
44
+ val itemSpacing = parser.toPixelFromDIP(map.getDouble("itemSpacing").toFloat().coerceAtLeast(0f))
43
45
 
44
46
  return ListStyle(
45
47
  fontSize,
@@ -56,6 +58,7 @@ data class ListStyle(
56
58
  markerFontWeight,
57
59
  gapWidth,
58
60
  marginLeft,
61
+ itemSpacing,
59
62
  )
60
63
  }
61
64
  }