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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/README.md +54 -8
  2. package/ReactNativeEnrichedMarkdown.podspec +82 -73
  3. package/android/build.gradle +186 -1
  4. package/android/consumer-rules.pro +1 -1
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +9 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +3 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +24 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +8 -0
  9. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +9 -0
  10. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +3 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +35 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +886 -23
  15. package/android/src/main/baseline-prof.txt +1 -1
  16. package/android/src/main/cpp/jni-adapter.cpp +74 -0
  17. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +189 -13
  18. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +17 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +50 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +48 -1
  21. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +10 -1
  22. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +42 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +82 -4
  24. package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyPressEvent.kt +24 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +54 -4
  26. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +901 -462
  27. package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
  30. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +101 -7
  34. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
  36. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
  39. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  65. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  66. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  68. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  69. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +30 -27
  77. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  78. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  79. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +27 -3
  80. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  90. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  91. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  93. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  94. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  95. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  96. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  97. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  100. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +37 -7
  101. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  102. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  103. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  104. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  105. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  106. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +49 -15
  117. package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +45 -0
  118. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +396 -0
  119. package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
  120. package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
  121. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +36 -9
  122. package/android/src/main/jni/CMakeLists.txt +53 -0
  123. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
  124. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
  125. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
  126. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
  127. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  128. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
  129. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
  130. package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
  131. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +7 -2
  132. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  133. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +6 -4
  134. package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
  135. package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
  136. package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
  137. package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
  138. package/cpp/highlight/HighlightGrammars.hpp +28 -0
  139. package/cpp/highlight/code_highlight_podspec.rb +211 -0
  140. package/cpp/highlight/gen-registry.mjs +155 -0
  141. package/cpp/highlight/grammar-versions.json +159 -0
  142. package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
  143. package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
  144. package/cpp/parser/MD4CParser.cpp +173 -61
  145. package/cpp/parser/MD4CParser.hpp +1 -0
  146. package/cpp/parser/MarkdownASTNode.hpp +2 -1
  147. package/cpp/wasm/ASTSerializer.cpp +2 -0
  148. package/cpp/wasm/build.sh +1 -0
  149. package/cpp/wasm/md4c_wasm.cpp +3 -1
  150. package/docs/ACCESSIBILITY.md +14 -2
  151. package/docs/API_REFERENCE.md +234 -12
  152. package/docs/BREAKING_CHANGES.md +129 -0
  153. package/docs/CODE_HIGHLIGHT.md +133 -0
  154. package/docs/COPY_OPTIONS.md +47 -3
  155. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  156. package/docs/IMAGE_CACHING.md +30 -0
  157. package/docs/INPUT.md +108 -11
  158. package/docs/LATEX_MATH.md +87 -45
  159. package/docs/MACOS.md +1 -1
  160. package/docs/MARKDOWN_STREAMING.md +27 -1
  161. package/docs/NATIVE_ASSETS.md +127 -0
  162. package/docs/RTL.md +1 -1
  163. package/docs/STYLES.md +46 -6
  164. package/docs/TESTING.md +112 -0
  165. package/docs/TEXT.md +16 -6
  166. package/docs/WEB.md +3 -1
  167. package/ios/EnrichedMarkdown.h +7 -0
  168. package/ios/EnrichedMarkdown.mm +284 -75
  169. package/ios/EnrichedMarkdownText.h +7 -0
  170. package/ios/EnrichedMarkdownText.mm +101 -73
  171. package/ios/attachments/ENRMImageAttachment.h +9 -0
  172. package/ios/attachments/ENRMImageAttachment.m +143 -40
  173. package/ios/attachments/ENRMMathInlineAttachment.h +2 -0
  174. package/ios/attachments/ENRMMathInlineAttachment.m +48 -81
  175. package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
  176. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  177. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  178. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +35 -0
  179. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +886 -23
  180. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +131 -0
  181. package/ios/input/ENRMAutoLinkDetector.h +2 -1
  182. package/ios/input/ENRMAutoLinkDetector.mm +79 -41
  183. package/ios/input/ENRMBlockEditCoordinator.h +71 -0
  184. package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
  185. package/ios/input/ENRMBlockRange.h +28 -0
  186. package/ios/input/ENRMBlockRange.mm +30 -0
  187. package/ios/input/ENRMBlockStore.h +56 -0
  188. package/ios/input/ENRMBlockStore.mm +275 -0
  189. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  190. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  191. package/ios/input/ENRMDetectorPipeline.h +2 -1
  192. package/ios/input/ENRMEditPipeline.h +76 -0
  193. package/ios/input/ENRMEditPipeline.mm +228 -0
  194. package/ios/input/ENRMEditSession.h +36 -0
  195. package/ios/input/ENRMEditSession.mm +63 -0
  196. package/ios/input/ENRMFormattingStore.h +17 -0
  197. package/ios/input/ENRMFormattingStore.mm +95 -105
  198. package/ios/input/ENRMInputBlockType.h +91 -0
  199. package/ios/input/ENRMInputBlockType.mm +5 -0
  200. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  201. package/ios/input/ENRMInputEventEmitter.h +56 -0
  202. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  203. package/ios/input/ENRMInputFormatter.h +29 -69
  204. package/ios/input/ENRMInputFormatter.mm +216 -124
  205. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  206. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  207. package/ios/input/ENRMInputLayoutManager.h +9 -0
  208. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  209. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  210. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  211. package/ios/input/ENRMInputParser.h +5 -6
  212. package/ios/input/ENRMInputParser.mm +461 -178
  213. package/ios/input/ENRMInputRemend.mm +2 -2
  214. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  215. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  216. package/ios/input/ENRMInputTextView.mm +103 -8
  217. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  218. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  219. package/ios/input/ENRMLinkCoordinator.h +41 -0
  220. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  221. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  222. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  223. package/ios/input/ENRMMathAtom.h +26 -0
  224. package/ios/input/ENRMMathAtom.mm +12 -0
  225. package/ios/input/ENRMMentionCoordinator.h +45 -0
  226. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  227. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  228. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  229. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  230. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  231. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  232. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  233. package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
  234. package/ios/input/InputStylePropsUtils.h +109 -68
  235. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  236. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  237. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  238. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  239. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  240. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  241. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  242. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  243. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  244. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  245. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  246. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  247. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  248. package/ios/internals/MeasurementCache.h +33 -7
  249. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  250. package/ios/math/ENRMMathChipController.h +74 -0
  251. package/ios/math/ENRMMathChipController.mm +476 -0
  252. package/ios/math/ENRMRaTeXBridge.swift +4 -1
  253. package/ios/parser/ENRMMarkdownParser.h +1 -8
  254. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  255. package/ios/parser/MarkdownASTNode.h +2 -1
  256. package/ios/parser/MarkdownParserBridge.mm +4 -0
  257. package/ios/renderer/AttributedRenderer.m +20 -2
  258. package/ios/renderer/BaseRenderer.h +17 -2
  259. package/ios/renderer/BaseRenderer.m +30 -0
  260. package/ios/renderer/BlockquoteRenderer.h +1 -1
  261. package/ios/renderer/BlockquoteRenderer.m +144 -35
  262. package/ios/renderer/CodeBlockRenderer.h +1 -1
  263. package/ios/renderer/CodeBlockRenderer.m +27 -23
  264. package/ios/renderer/CodeRenderer.h +1 -1
  265. package/ios/renderer/CodeRenderer.m +3 -1
  266. package/ios/renderer/ENRMImageRenderer.h +1 -1
  267. package/ios/renderer/ENRMImageRenderer.m +5 -1
  268. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  269. package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
  270. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  271. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  272. package/ios/renderer/EmphasisRenderer.h +1 -1
  273. package/ios/renderer/EmphasisRenderer.m +3 -1
  274. package/ios/renderer/HeadingRenderer.h +1 -1
  275. package/ios/renderer/HeadingRenderer.m +13 -6
  276. package/ios/renderer/HighlightRenderer.h +1 -1
  277. package/ios/renderer/HighlightRenderer.m +3 -1
  278. package/ios/renderer/LinkRenderer.h +1 -1
  279. package/ios/renderer/LinkRenderer.m +3 -1
  280. package/ios/renderer/ListItemRenderer.h +13 -1
  281. package/ios/renderer/ListItemRenderer.m +102 -44
  282. package/ios/renderer/ListRenderer.h +2 -2
  283. package/ios/renderer/ListRenderer.m +63 -25
  284. package/ios/renderer/ParagraphRenderer.h +1 -1
  285. package/ios/renderer/ParagraphRenderer.m +16 -8
  286. package/ios/renderer/RenderContext.h +10 -0
  287. package/ios/renderer/RenderContext.m +44 -0
  288. package/ios/renderer/RendererFactory.m +14 -0
  289. package/ios/renderer/StrikethroughRenderer.h +1 -1
  290. package/ios/renderer/StrikethroughRenderer.m +3 -1
  291. package/ios/renderer/StrongRenderer.h +1 -1
  292. package/ios/renderer/StrongRenderer.m +3 -1
  293. package/ios/renderer/SubscriptRenderer.h +1 -1
  294. package/ios/renderer/SubscriptRenderer.m +3 -1
  295. package/ios/renderer/SuperscriptRenderer.h +1 -1
  296. package/ios/renderer/SuperscriptRenderer.m +3 -1
  297. package/ios/renderer/TextRenderer.h +2 -2
  298. package/ios/renderer/TextRenderer.m +3 -1
  299. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  300. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  301. package/ios/renderer/UnderlineRenderer.h +1 -1
  302. package/ios/renderer/UnderlineRenderer.m +3 -1
  303. package/ios/styles/StyleConfig.h +22 -0
  304. package/ios/styles/StyleConfig.mm +132 -0
  305. package/ios/utils/BlockquoteBorder.h +6 -0
  306. package/ios/utils/BlockquoteBorder.m +249 -52
  307. package/ios/utils/CodeBlockBackground.m +16 -2
  308. package/ios/utils/ENRMAtomicSize.h +65 -0
  309. package/ios/utils/ENRMBlockMenu.h +57 -0
  310. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  311. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  312. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  313. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  314. package/ios/utils/ENRMFeatureFlags.h +7 -0
  315. package/ios/utils/ENRMImageDownloader.h +11 -1
  316. package/ios/utils/ENRMImageDownloader.m +60 -16
  317. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  318. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  319. package/ios/utils/ENRMMathFallback.h +26 -0
  320. package/ios/utils/ENRMMathFallback.m +18 -0
  321. package/ios/utils/ENRMTextViewSetup.h +35 -17
  322. package/ios/utils/EditMenuUtils.h +22 -5
  323. package/ios/utils/EditMenuUtils.m +53 -9
  324. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  325. package/ios/utils/LastElementUtils.h +1 -0
  326. package/ios/utils/ListMarkerDrawer.m +29 -42
  327. package/ios/utils/MarkdownASTSerializer.m +4 -0
  328. package/ios/utils/ParagraphStyleUtils.h +5 -0
  329. package/ios/utils/ParagraphStyleUtils.m +77 -17
  330. package/ios/utils/RenderedMarkdownSegment.h +13 -1
  331. package/ios/utils/RenderedMarkdownSegment.m +20 -0
  332. package/ios/utils/SegmentRenderer.m +17 -4
  333. package/ios/utils/StreamingMarkdownFilter.h +11 -1
  334. package/ios/utils/StreamingMarkdownFilter.m +98 -2
  335. package/ios/utils/StylePropsUtils.h +63 -0
  336. package/ios/utils/TaskListTapUtils.m +24 -0
  337. package/ios/views/ENRMCodeBlockContainerView.h +53 -0
  338. package/ios/views/ENRMCodeBlockContainerView.m +559 -0
  339. package/ios/views/ENRMMathContainerView.h +16 -0
  340. package/ios/views/ENRMMathContainerView.m +74 -19
  341. package/ios/views/TableContainerView.h +22 -0
  342. package/ios/views/TableContainerView.m +261 -175
  343. package/jest.d.ts +1 -0
  344. package/jest.js +5 -0
  345. package/lib/module/EnrichedMarkdownNativeComponent.ts +73 -0
  346. package/lib/module/EnrichedMarkdownTextInput.js +84 -5
  347. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  348. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  349. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +74 -0
  350. package/lib/module/headingDefaults.js +34 -0
  351. package/lib/module/headingDefaults.js.map +1 -0
  352. package/lib/module/index.js.map +1 -1
  353. package/lib/module/jest/index.js +191 -0
  354. package/lib/module/jest/index.js.map +1 -0
  355. package/lib/module/native/EnrichedMarkdownText.js +37 -5
  356. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  357. package/lib/module/normalizeMarkdownStyle.js +81 -19
  358. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  359. package/lib/module/normalizeMarkdownStyle.web.js +56 -18
  360. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  361. package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
  362. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  363. package/lib/module/normalizeMenuItem.js +1 -1
  364. package/lib/module/utils/textInputState.js +26 -0
  365. package/lib/module/utils/textInputState.js.map +1 -0
  366. package/lib/module/web/EnrichedMarkdownText.js +9 -5
  367. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  368. package/lib/module/web/parseMarkdown.js +4 -3
  369. package/lib/module/web/parseMarkdown.js.map +1 -1
  370. package/lib/module/web/renderers/InlineRenderers.js +6 -0
  371. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  372. package/lib/module/web/renderers/ListRenderers.js +7 -2
  373. package/lib/module/web/renderers/ListRenderers.js.map +1 -1
  374. package/lib/module/web/renderers/index.js +7 -4
  375. package/lib/module/web/renderers/index.js.map +1 -1
  376. package/lib/module/web/styles.js +83 -6
  377. package/lib/module/web/styles.js.map +1 -1
  378. package/lib/module/web/utils.js +1 -0
  379. package/lib/module/web/utils.js.map +1 -1
  380. package/lib/module/web/wasm/md4c.js +0 -0
  381. package/lib/module/web/wasm/md4c.js.map +1 -1
  382. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +66 -0
  383. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  384. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
  385. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  386. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
  387. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  388. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +66 -0
  389. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  390. package/lib/typescript/src/headingDefaults.d.ts +9 -0
  391. package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
  392. package/lib/typescript/src/index.d.ts +2 -2
  393. package/lib/typescript/src/index.d.ts.map +1 -1
  394. package/lib/typescript/src/index.web.d.ts +1 -1
  395. package/lib/typescript/src/index.web.d.ts.map +1 -1
  396. package/lib/typescript/src/jest/index.d.ts +6 -0
  397. package/lib/typescript/src/jest/index.d.ts.map +1 -0
  398. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
  399. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  400. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  401. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  402. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
  403. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  404. package/lib/typescript/src/types/MarkdownStyle.d.ts +72 -0
  405. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  406. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +27 -0
  407. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  408. package/lib/typescript/src/types/MarkdownTextProps.d.ts +104 -9
  409. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  410. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +19 -0
  411. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  412. package/lib/typescript/src/types/events.d.ts +19 -0
  413. package/lib/typescript/src/types/events.d.ts.map +1 -1
  414. package/lib/typescript/src/utils/textInputState.d.ts +24 -0
  415. package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
  416. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  417. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  418. package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
  419. package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
  420. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  421. package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
  422. package/lib/typescript/src/web/renderers/index.d.ts +2 -1
  423. package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
  424. package/lib/typescript/src/web/styles.d.ts +2 -1
  425. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  426. package/lib/typescript/src/web/types.d.ts +6 -2
  427. package/lib/typescript/src/web/types.d.ts.map +1 -1
  428. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  429. package/package.json +36 -9
  430. package/postinstall.mjs +131 -0
  431. package/ratex-version.json +21 -0
  432. package/src/EnrichedMarkdownNativeComponent.ts +73 -0
  433. package/src/EnrichedMarkdownTextInput.tsx +159 -8
  434. package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  435. package/src/EnrichedMarkdownTextNativeComponent.ts +74 -0
  436. package/src/headingDefaults.ts +21 -0
  437. package/src/index.tsx +9 -0
  438. package/src/index.web.tsx +1 -0
  439. package/src/jest/index.tsx +215 -0
  440. package/src/native/EnrichedMarkdownText.tsx +53 -2
  441. package/src/normalizeMarkdownStyle.ts +102 -16
  442. package/src/normalizeMarkdownStyle.web.ts +63 -15
  443. package/src/normalizeMarkdownTextInputStyle.ts +110 -35
  444. package/src/normalizeMenuItem.ts +1 -1
  445. package/src/types/MarkdownStyle.ts +74 -0
  446. package/src/types/MarkdownStyleInternal.ts +39 -0
  447. package/src/types/MarkdownTextProps.ts +101 -9
  448. package/src/types/MarkdownTextProps.web.ts +19 -0
  449. package/src/types/events.ts +21 -0
  450. package/src/utils/textInputState.ts +35 -0
  451. package/src/web/EnrichedMarkdownText.tsx +16 -2
  452. package/src/web/parseMarkdown.ts +6 -2
  453. package/src/web/renderers/InlineRenderers.tsx +5 -0
  454. package/src/web/renderers/ListRenderers.tsx +14 -2
  455. package/src/web/renderers/index.tsx +6 -2
  456. package/src/web/styles.ts +89 -5
  457. package/src/web/types.ts +6 -1
  458. package/src/web/utils.ts +1 -0
  459. package/src/web/wasm/md4c.js +0 -0
  460. package/vendor-grammars.mjs +511 -0
  461. package/vendor-ratex.mjs +173 -0
  462. package/app.plugin.js +0 -2
  463. package/plugin/build/withAndroidMath.js +0 -19
  464. package/plugin/build/withIosMath.js +0 -28
  465. package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
  466. package/plugin/src/withAndroidMath.ts +0 -24
  467. package/plugin/src/withIosMath.ts +0 -35
  468. package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
  469. package/plugin/tsconfig.build.json +0 -16
@@ -80,6 +80,7 @@ class RendererFactory(
80
80
 
81
81
  private val textRenderer = TextRenderer()
82
82
  private val lineBreakRenderer = LineBreakRenderer()
83
+ private val softBreakRenderer = SoftBreakRenderer()
83
84
 
84
85
  private val renderers: Map<MarkdownASTNode.NodeType, NodeRenderer> by lazy {
85
86
  buildMap {
@@ -103,13 +104,20 @@ class RendererFactory(
103
104
  put(MarkdownASTNode.NodeType.Code, CodeRenderer(config))
104
105
  put(MarkdownASTNode.NodeType.Image, ImageRenderer())
105
106
  put(MarkdownASTNode.NodeType.LineBreak, lineBreakRenderer)
107
+ put(MarkdownASTNode.NodeType.SoftBreak, softBreakRenderer)
106
108
  put(MarkdownASTNode.NodeType.ThematicBreak, ThematicBreakRenderer(config))
107
109
  put(MarkdownASTNode.NodeType.Spoiler, SpoilerRenderer())
108
110
  if (FeatureFlags.IS_MATH_ENABLED) {
109
111
  try {
110
112
  val mathInlineRendererClass = Class.forName("com.swmansion.enriched.markdown.renderer.MathInlineRenderer")
111
113
  val constructor = mathInlineRendererClass.getConstructor(RendererConfig::class.java, Context::class.java)
112
- put(MarkdownASTNode.NodeType.LatexMathInline, constructor.newInstance(config, context) as NodeRenderer)
114
+ val mathInlineRenderer = constructor.newInstance(config, context) as NodeRenderer
115
+ put(MarkdownASTNode.NodeType.LatexMathInline, mathInlineRenderer)
116
+ // Isolated display math is promoted to a block segment in the parser
117
+ // (see promoteDisplayMathFromParagraphs). What reaches the factory is
118
+ // genuinely mid-line display math (e.g. `a $$x$$ b`); render it inline
119
+ // rather than dropping it via the TextRenderer fallback.
120
+ put(MarkdownASTNode.NodeType.LatexMathDisplay, mathInlineRenderer)
113
121
  } catch (_: Exception) {
114
122
  // math not available
115
123
  }
@@ -0,0 +1,16 @@
1
+ package com.swmansion.enriched.markdown.renderer
2
+
3
+ import android.text.SpannableStringBuilder
4
+ import com.swmansion.enriched.markdown.parser.MarkdownASTNode
5
+
6
+ class SoftBreakRenderer : NodeRenderer {
7
+ override fun render(
8
+ node: MarkdownASTNode,
9
+ builder: SpannableStringBuilder,
10
+ onLinkPress: ((String) -> Unit)?,
11
+ onLinkLongPress: ((String) -> Unit)?,
12
+ factory: RendererFactory,
13
+ ) {
14
+ builder.append(" ")
15
+ }
16
+ }
@@ -1,6 +1,9 @@
1
1
  package com.swmansion.enriched.markdown.renderer
2
2
 
3
+ import android.content.res.AssetManager
3
4
  import android.graphics.Typeface
5
+ import com.facebook.react.common.ReactConstants
6
+ import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
4
7
  import com.swmansion.enriched.markdown.styles.LinkVariantEntry
5
8
  import com.swmansion.enriched.markdown.styles.StyleConfig
6
9
 
@@ -8,6 +11,8 @@ import com.swmansion.enriched.markdown.styles.StyleConfig
8
11
  class SpanStyleCache(
9
12
  style: StyleConfig,
10
13
  ) {
14
+ private val assetManager: AssetManager = style.assetManager
15
+
11
16
  // Colors to preserve when applying inline styles (links, code, strong, emphasis)
12
17
  val colorsToPreserve: IntArray = buildColorsToPreserve(style)
13
18
 
@@ -95,36 +100,34 @@ class SpanStyleCache(
95
100
  return compiledVariantPatterns.firstOrNull { (regex, _) -> regex.containsMatchIn(url) }?.second
96
101
  }
97
102
 
103
+ /**
104
+ * Cached typeface for font family + style (BOLD, ITALIC, BOLD_ITALIC).
105
+ *
106
+ * Loads the base custom font at its NORMAL weight (via applyStyles/ReactFontManager)
107
+ * so the bundled assets/fonts file resolves, then synthesizes bold/italic on that real
108
+ * typeface. Requesting bold/italic directly makes ReactFontManager look for `_bold`/
109
+ * `_italic` asset variants that single-file custom fonts don't have — and it then falls
110
+ * back to a default system face, dropping the custom font entirely.
111
+ */
112
+ fun getTypeface(
113
+ fontFamily: String,
114
+ style: Int,
115
+ ): Typeface =
116
+ typefaceCache.getOrPut("family|$fontFamily|$style") {
117
+ val base =
118
+ applyStyles(
119
+ null,
120
+ ReactConstants.UNSET,
121
+ ReactConstants.UNSET,
122
+ fontFamily.takeIf { it.isNotEmpty() },
123
+ assetManager,
124
+ )
125
+ if (style == Typeface.NORMAL) base else Typeface.create(base, style)
126
+ }
127
+
98
128
  companion object {
99
129
  private val typefaceCache = mutableMapOf<String, Typeface>()
100
130
 
101
- /** Cached typeface for font family + style (BOLD, ITALIC, BOLD_ITALIC) */
102
- fun getTypeface(
103
- fontFamily: String,
104
- style: Int,
105
- ): Typeface =
106
- typefaceCache.getOrPut("$fontFamily|$style") {
107
- val base =
108
- fontFamily
109
- .takeIf { it.isNotEmpty() }
110
- ?.let { Typeface.create(it, Typeface.NORMAL) }
111
- ?: Typeface.DEFAULT
112
- Typeface.create(base, style)
113
- }
114
-
115
- /** Cached typeface using weight string (e.g., "bold", "700") */
116
- fun getTypefaceWithWeight(
117
- fontFamily: String,
118
- fontWeight: String,
119
- ): Typeface {
120
- val style =
121
- when (fontWeight.lowercase()) {
122
- "bold", "700", "800", "900" -> Typeface.BOLD
123
- else -> Typeface.NORMAL
124
- }
125
- return getTypeface(fontFamily, style)
126
- }
127
-
128
131
  /** Cached monospace typeface preserving bold/italic */
129
132
  fun getMonospaceTypeface(currentStyle: Int): Typeface =
130
133
  typefaceCache.getOrPut("monospace|$currentStyle") {
@@ -22,11 +22,20 @@ abstract class BaseListSpan(
22
22
  protected val blockStyle: BlockStyle,
23
23
  protected val marginLeft: Float,
24
24
  protected val gapWidth: Float,
25
+ internal val drawsMarker: Boolean = true,
25
26
  ) : MetricAffectingSpan(),
26
27
  LeadingMarginSpan {
27
28
  private var cachedText: CharSequence? = null
28
29
  private var cachedHasDeeperSpanByPosition = mutableMapOf<Int, Boolean>()
29
30
 
31
+ val markerFontMetrics: Paint.FontMetrics by lazy {
32
+ TextPaint()
33
+ .apply {
34
+ applyBlockStyleFont(blockStyle, context)
35
+ textSize = blockStyle.fontSize
36
+ }.fontMetrics
37
+ }
38
+
30
39
  protected abstract fun getMarkerWidth(): Float
31
40
 
32
41
  override fun updateMeasureState(textPaint: TextPaint) = applyTextStyle(textPaint)
@@ -55,6 +64,7 @@ abstract class BaseListSpan(
55
64
  first: Boolean,
56
65
  layout: Layout?,
57
66
  ) {
67
+ if (!drawsMarker) return
58
68
  if (!first || shouldSkipDrawing(text, start) || !hasContent(text, start, end)) return
59
69
 
60
70
  // Only draw the marker on the true first line of this span.
@@ -62,6 +72,20 @@ abstract class BaseListSpan(
62
72
  // but only the line at the span start should get a bullet/number.
63
73
  if (text is Spanned && text.getSpanStart(this) != start) return
64
74
 
75
+ drawMarkerAt(canvas, paint, x, dir, top, baseline, bottom, layout, start)
76
+ }
77
+
78
+ fun drawMarkerAt(
79
+ canvas: Canvas,
80
+ paint: Paint,
81
+ x: Int,
82
+ dir: Int,
83
+ top: Int,
84
+ baseline: Int,
85
+ bottom: Int,
86
+ layout: Layout?,
87
+ start: Int,
88
+ ) {
65
89
  val originalStyle = paint.style
66
90
  val originalColor = paint.color
67
91
  drawMarker(canvas, paint, x, dir, top, baseline, bottom, layout, start)
@@ -5,6 +5,8 @@ import android.content.Context
5
5
  import android.graphics.Canvas
6
6
  import android.graphics.Color
7
7
  import android.graphics.Paint
8
+ import android.graphics.Path
9
+ import android.graphics.RectF
8
10
  import android.graphics.Typeface
9
11
  import android.text.Layout
10
12
  import android.text.Spanned
@@ -12,6 +14,7 @@ import android.text.TextPaint
12
14
  import android.text.style.LeadingMarginSpan
13
15
  import android.text.style.LineBackgroundSpan
14
16
  import android.text.style.MetricAffectingSpan
17
+ import androidx.core.graphics.withSave
15
18
  import com.swmansion.enriched.markdown.renderer.BlockStyle
16
19
  import com.swmansion.enriched.markdown.renderer.SpanStyleCache
17
20
  import com.swmansion.enriched.markdown.styles.BlockquoteStyle
@@ -37,7 +40,13 @@ class BlockquoteSpan(
37
40
 
38
41
  // Cache for shouldSkipDrawing to avoid repeated getSpans() calls during draw passes
39
42
  private var cachedText: CharSequence? = null
40
- private var cachedMaxDepthByPosition = mutableMapOf<Int, Int>()
43
+ private var cachedSpansByPosition = mutableMapOf<Int, Array<BlockquoteSpan>>()
44
+ private var boxLeft = 0f
45
+ private var boxRight = 0f
46
+
47
+ private val path = Path()
48
+ private val rect = RectF()
49
+ private val radiiArray = FloatArray(8)
41
50
 
42
51
  override fun updateMeasureState(tp: TextPaint) = applyTextStyle(tp)
43
52
 
@@ -63,13 +72,56 @@ class BlockquoteSpan(
63
72
  if (shouldSkipDrawing(text, start)) return
64
73
 
65
74
  val borderPaint = configureBorderPaint()
66
- val borderTop = top.toFloat()
67
- val borderBottom = bottom.toFloat()
75
+ val radius = blockquoteStyle.borderRadius
76
+ val spanned = text as? Spanned
77
+
78
+ if (spanned == null) {
79
+ drawBorders(c, x, dir, top, bottom, borderPaint)
80
+ return
81
+ }
82
+
83
+ val rootSpan = spanAtMinDepth(spanned, start)
84
+ val clipToRoot =
85
+ radius > 0f && boxRight > boxLeft && rootSpan != null && isBoundaryLine(spanned, start, end, rootSpan)
86
+
87
+ val padding = blockquoteStyle.padding
88
+ var topInset = 0f
89
+ var bottomInset = 0f
68
90
 
69
91
  for (level in 0..depth) {
70
92
  val borderX = x + (levelSpacing * level * dir)
71
93
  val borderRight = borderX + (blockquoteStyle.borderWidth * dir)
72
- c.drawRect(minOf(borderX, borderRight), borderTop, maxOf(borderX, borderRight), borderBottom, borderPaint)
94
+ val stripeLeft = minOf(borderX, borderRight)
95
+ val stripeRight = maxOf(borderX, borderRight)
96
+ val stripeTop = top.toFloat() + topInset
97
+ val stripeBottom = bottom.toFloat() - bottomInset
98
+
99
+ val levelSpan = if (level == 0) rootSpan else spanAtDepth(spanned, start, level)
100
+ val clipToOwn =
101
+ radius > 0f && level > 0 && levelSpan != null && isBoundaryLine(spanned, start, end, levelSpan)
102
+
103
+ if (!clipToRoot && !clipToOwn) {
104
+ c.drawRect(stripeLeft, stripeTop, stripeRight, stripeBottom, borderPaint)
105
+ } else {
106
+ c.withSave {
107
+ if (clipToRoot) {
108
+ buildBoundaryPath(spanned, start, end, rootSpan!!, boxLeft, top.toFloat(), boxRight, bottom.toFloat())
109
+ clipPath(path)
110
+ }
111
+ if (clipToOwn) {
112
+ val clipLeft = if (dir >= 0) stripeLeft else boxLeft
113
+ val clipRight = if (dir >= 0) boxRight else stripeRight
114
+ buildBoundaryPath(spanned, start, end, levelSpan!!, clipLeft, stripeTop, clipRight, stripeBottom)
115
+ clipPath(path)
116
+ }
117
+ drawRect(stripeLeft, stripeTop, stripeRight, stripeBottom, borderPaint)
118
+ }
119
+ }
120
+
121
+ if (padding > 0 && levelSpan != null) {
122
+ if (start == spanned.getSpanStart(levelSpan)) topInset += padding
123
+ if (isLastLineOf(spanned, end, levelSpan)) bottomInset += padding
124
+ }
73
125
  }
74
126
  }
75
127
 
@@ -87,7 +139,40 @@ class BlockquoteSpan(
87
139
  lineNum: Int,
88
140
  ) {
89
141
  if (shouldSkipDrawing(text, start)) return
90
- drawBackground(canvas, left, top, bottom, right)
142
+
143
+ boxLeft = left.toFloat()
144
+ boxRight = right.toFloat()
145
+
146
+ val bgColor = blockquoteStyle.backgroundColor?.takeIf { it != Color.TRANSPARENT } ?: return
147
+ val backgroundPaint = configureBackgroundPaint(bgColor)
148
+ val radius = blockquoteStyle.borderRadius
149
+ val rootSpan = (text as? Spanned)?.let { spanAtMinDepth(it, start) }
150
+
151
+ if (radius <= 0f || rootSpan == null) {
152
+ canvas.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), backgroundPaint)
153
+ return
154
+ }
155
+
156
+ buildBoundaryPath(text as Spanned, start, end, rootSpan, left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
157
+ canvas.drawPath(path, backgroundPaint)
158
+ }
159
+
160
+ private fun drawBorders(
161
+ c: Canvas,
162
+ x: Int,
163
+ dir: Int,
164
+ top: Int,
165
+ bottom: Int,
166
+ borderPaint: Paint,
167
+ ) {
168
+ val borderTop = top.toFloat()
169
+ val borderBottom = bottom.toFloat()
170
+
171
+ for (level in 0..depth) {
172
+ val borderX = x + (levelSpacing * level * dir)
173
+ val borderRight = borderX + (blockquoteStyle.borderWidth * dir)
174
+ c.drawRect(minOf(borderX, borderRight), borderTop, maxOf(borderX, borderRight), borderBottom, borderPaint)
175
+ }
91
176
  }
92
177
 
93
178
  @SuppressLint("WrongConstant") // Result of mask is always valid: 0, 1, 2, or 3
@@ -118,35 +203,100 @@ class BlockquoteSpan(
118
203
  color = bgColor
119
204
  }
120
205
 
206
+ private fun spansAt(
207
+ text: Spanned,
208
+ start: Int,
209
+ ): Array<BlockquoteSpan> {
210
+ if (cachedText !== text) {
211
+ cachedText = text
212
+ cachedSpansByPosition.clear()
213
+ }
214
+ return cachedSpansByPosition.getOrPut(start) {
215
+ text.getSpans(start, start + 1, BlockquoteSpan::class.java)
216
+ }
217
+ }
218
+
121
219
  private fun shouldSkipDrawing(
122
220
  text: CharSequence?,
123
221
  start: Int,
124
222
  ): Boolean {
125
223
  if (text !is Spanned) return false
224
+ val maxDepth = spansAt(text, start).maxOfOrNull { it.depth } ?: -1
225
+ return maxDepth > depth
226
+ }
126
227
 
127
- if (cachedText !== text) {
128
- cachedText = text
129
- cachedMaxDepthByPosition.clear()
130
- }
228
+ private fun spanAtMinDepth(
229
+ text: Spanned,
230
+ start: Int,
231
+ ): BlockquoteSpan? = spansAt(text, start).minByOrNull { it.depth }
131
232
 
132
- val maxDepth =
133
- cachedMaxDepthByPosition.getOrPut(start) {
134
- val spans = text.getSpans(start, start + 1, BlockquoteSpan::class.java)
135
- spans.maxOfOrNull { it.depth } ?: -1
136
- }
233
+ private fun spanAtDepth(
234
+ text: Spanned,
235
+ start: Int,
236
+ level: Int,
237
+ ): BlockquoteSpan? = spansAt(text, start).firstOrNull { it.depth == level }
137
238
 
138
- return maxDepth > depth
239
+ /**
240
+ * The span range can include trailing '\n'(s) that form no visible line (e.g. a
241
+ * paragraph break closing the quote), so the last drawn line ends before getSpanEnd;
242
+ * this compares against the end of visible content instead.
243
+ */
244
+ private fun isLastLineOf(
245
+ text: Spanned,
246
+ lineEnd: Int,
247
+ box: BlockquoteSpan,
248
+ ): Boolean {
249
+ var boxEnd = text.getSpanEnd(box)
250
+ while (boxEnd > 0 && text[boxEnd - 1] == '\n') {
251
+ boxEnd--
252
+ }
253
+ return lineEnd >= boxEnd
139
254
  }
140
255
 
141
- private fun drawBackground(
142
- c: Canvas,
143
- left: Int,
144
- top: Int,
145
- bottom: Int,
146
- right: Int,
256
+ private fun isBoundaryLine(
257
+ text: Spanned,
258
+ lineStart: Int,
259
+ lineEnd: Int,
260
+ box: BlockquoteSpan,
261
+ ): Boolean {
262
+ val boxStart = text.getSpanStart(box)
263
+ val boxEnd = text.getSpanEnd(box)
264
+ if (boxStart !in 0 until boxEnd) return false
265
+ return lineStart == boxStart || isLastLineOf(text, lineEnd, box)
266
+ }
267
+
268
+ private fun buildBoundaryPath(
269
+ text: Spanned,
270
+ lineStart: Int,
271
+ lineEnd: Int,
272
+ box: BlockquoteSpan,
273
+ left: Float,
274
+ top: Float,
275
+ right: Float,
276
+ bottom: Float,
147
277
  ) {
148
- val bgColor = blockquoteStyle.backgroundColor?.takeIf { it != Color.TRANSPARENT } ?: return
149
- val backgroundPaint = configureBackgroundPaint(bgColor)
150
- c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), backgroundPaint)
278
+ val boxStart = text.getSpanStart(box)
279
+
280
+ val isFirstLine = lineStart == boxStart
281
+ val isLastLine = isLastLineOf(text, lineEnd, box)
282
+
283
+ val radius = blockquoteStyle.borderRadius
284
+ radiiArray.fill(0f)
285
+ if (isFirstLine) {
286
+ radiiArray[0] = radius
287
+ radiiArray[1] = radius // Top-Left
288
+ radiiArray[2] = radius
289
+ radiiArray[3] = radius // Top-Right
290
+ }
291
+ if (isLastLine) {
292
+ radiiArray[4] = radius
293
+ radiiArray[5] = radius // Bottom-Right
294
+ radiiArray[6] = radius
295
+ radiiArray[7] = radius // Bottom-Left
296
+ }
297
+
298
+ rect.set(left, top, right, bottom)
299
+ path.reset()
300
+ path.addRoundRect(rect, radiiArray, Path.Direction.CW)
151
301
  }
152
302
  }
@@ -7,6 +7,7 @@ import android.graphics.RectF
7
7
  import android.text.Spanned
8
8
  import android.text.StaticLayout
9
9
  import android.text.TextPaint
10
+ import android.text.style.LeadingMarginSpan
10
11
  import android.text.style.LineBackgroundSpan
11
12
  import com.swmansion.enriched.markdown.styles.StyleConfig
12
13
  import kotlin.math.max
@@ -58,8 +59,9 @@ class CodeBackgroundSpan(
58
59
 
59
60
  // 2. Calculate coordinates
60
61
  val finalBottom = adjustBottomForMargin(text, end, bottom)
61
- val startX = if (isFirst) getHorizontalOffset(text, start, end, spanStart, p) + left else left.toFloat()
62
- val endX = if (isLast) getHorizontalOffset(text, start, end, spanEnd, p) + left else right.toFloat()
62
+ val leadingMargin = leadingMarginAt(text, start)
63
+ val startX = if (isFirst) getHorizontalOffset(text, start, end, spanStart, p, leadingMargin) + left else left.toFloat() + leadingMargin
64
+ val endX = if (isLast) getHorizontalOffset(text, start, end, spanEnd, p, leadingMargin) + left else right.toFloat()
63
65
 
64
66
  rect.set(min(startX, endX), top.toFloat(), max(startX, endX), finalBottom.toFloat())
65
67
 
@@ -71,14 +73,23 @@ class CodeBackgroundSpan(
71
73
  drawShapes(canvas, isFirst, isLast)
72
74
  }
73
75
 
76
+ /**
77
+ * Returns the x position of [index] relative to the line's left edge, including any
78
+ * leading margin. The measuring StaticLayout is built from a subSequence that keeps
79
+ * all spans, so LeadingMarginSpans (lists, blockquotes) are already applied to
80
+ * getPrimaryHorizontal; adding the margin again on top would shift the background
81
+ * right by the indent. The margin is only added explicitly in the early-return case,
82
+ * where no layout is built.
83
+ */
74
84
  private fun getHorizontalOffset(
75
85
  text: CharSequence,
76
86
  lineStart: Int,
77
87
  lineEnd: Int,
78
88
  index: Int,
79
89
  paint: Paint,
90
+ leadingMargin: Int,
80
91
  ): Float {
81
- if (index <= lineStart) return 0f
92
+ if (index <= lineStart) return leadingMargin.toFloat()
82
93
  val lineText = text.subSequence(lineStart, lineEnd)
83
94
  val textPaint = paint as? TextPaint ?: TextPaint(paint)
84
95
  val layout = StaticLayout.Builder.obtain(lineText, 0, lineText.length, textPaint, 10000).build()
@@ -164,6 +175,19 @@ class CodeBackgroundSpan(
164
175
  }
165
176
  }
166
177
 
178
+ private fun leadingMarginAt(
179
+ text: Spanned,
180
+ lineStart: Int,
181
+ ): Int {
182
+ if (lineStart >= text.length) return 0
183
+ val spans = text.getSpans(lineStart, lineStart + 1, LeadingMarginSpan::class.java)
184
+ var margin = 0
185
+ for (span in spans) {
186
+ margin += span.getLeadingMargin(false)
187
+ }
188
+ return margin
189
+ }
190
+
167
191
  private fun adjustBottomForMargin(
168
192
  text: Spanned,
169
193
  lineEnd: Int,
@@ -12,27 +12,25 @@ import android.text.style.LeadingMarginSpan
12
12
  import android.text.style.LineBackgroundSpan
13
13
  import android.text.style.MetricAffectingSpan
14
14
  import androidx.core.graphics.withSave
15
- import com.swmansion.enriched.markdown.renderer.BlockStyle
16
15
  import com.swmansion.enriched.markdown.renderer.SpanStyleCache
17
16
  import com.swmansion.enriched.markdown.styles.CodeBlockStyle
18
- import com.swmansion.enriched.markdown.utils.text.extensions.applyBlockStyleFont
17
+ import com.swmansion.enriched.markdown.utils.text.extensions.applyCodeBlockTextStyle
19
18
  import com.swmansion.enriched.markdown.utils.text.extensions.applyColorPreserving
20
19
 
20
+ /**
21
+ * Draws the commonmark-flavor code block box as per-line strips. The visual
22
+ * conventions (half-stroke border inset, radius reduced by that inset) must
23
+ * stay in sync with the whole-rect box the github flavor draws in
24
+ * CodeBlockContainerView.
25
+ */
21
26
  class CodeBlockSpan(
22
27
  private val codeBlockStyle: CodeBlockStyle,
23
28
  private val context: Context,
24
29
  private val styleCache: SpanStyleCache,
30
+ private val leadingIndent: Int = 0,
25
31
  ) : MetricAffectingSpan(),
26
32
  LineBackgroundSpan,
27
33
  LeadingMarginSpan {
28
- private val blockStyle =
29
- BlockStyle(
30
- fontSize = codeBlockStyle.fontSize,
31
- fontFamily = codeBlockStyle.fontFamily,
32
- fontWeight = codeBlockStyle.fontWeight,
33
- color = codeBlockStyle.color,
34
- )
35
-
36
34
  private val path = Path()
37
35
  private val rect = RectF()
38
36
  private val arcRect = RectF()
@@ -59,7 +57,7 @@ class CodeBlockSpan(
59
57
  color = codeBlockStyle.borderColor
60
58
  }
61
59
 
62
- override fun getLeadingMargin(first: Boolean): Int = codeBlockStyle.padding.toInt()
60
+ override fun getLeadingMargin(first: Boolean): Int = codeBlockStyle.padding.toInt() + leadingIndent
63
61
 
64
62
  override fun drawLeadingMargin(
65
63
  c: Canvas?,
@@ -105,11 +103,12 @@ class CodeBlockSpan(
105
103
  val inset = codeBlockStyle.borderWidth / 2f
106
104
 
107
105
  rect.set(
108
- left.toFloat() + inset,
106
+ left.toFloat() + leadingIndent + inset,
109
107
  top.toFloat() + (if (isFirstLine) inset else 0f),
110
108
  right.toFloat() - inset,
111
109
  bottom.toFloat() - (if (isLastLine) inset else 0f),
112
110
  )
111
+ if (rect.left >= rect.right) return
113
112
 
114
113
  val radius = codeBlockStyle.borderRadius
115
114
  val adjRadius = if (radius > inset) radius - inset else radius
@@ -187,10 +186,8 @@ class CodeBlockSpan(
187
186
  }
188
187
 
189
188
  private fun applyTextStyle(tp: TextPaint) {
190
- tp.textSize = blockStyle.fontSize
191
-
192
- tp.applyBlockStyleFont(blockStyle, context)
189
+ tp.applyCodeBlockTextStyle(codeBlockStyle, context)
193
190
 
194
- tp.applyColorPreserving(blockStyle.color, *styleCache.colorsToPreserve)
191
+ tp.applyColorPreserving(codeBlockStyle.color, *styleCache.colorsToPreserve)
195
192
  }
196
193
  }
@@ -24,7 +24,7 @@ class CodeSpan(
24
24
  val preservedStyle = (paint.typeface?.style ?: 0) and (Typeface.BOLD or Typeface.ITALIC)
25
25
  paint.typeface =
26
26
  if (styleCache.codeFontFamily.isNotEmpty()) {
27
- SpanStyleCache.getTypeface(styleCache.codeFontFamily, preservedStyle)
27
+ styleCache.getTypeface(styleCache.codeFontFamily, preservedStyle)
28
28
  } else {
29
29
  SpanStyleCache.getMonospaceTypeface(preservedStyle)
30
30
  }
@@ -27,7 +27,7 @@ class EmphasisSpan(
27
27
  tp.typeface =
28
28
  if (styleCache.emphasisFontFamily.isNotEmpty()) {
29
29
  val resolvedStyle = if (styleCache.emphasisFontStyle == "normal") Typeface.NORMAL else style
30
- SpanStyleCache.getTypeface(styleCache.emphasisFontFamily, resolvedStyle)
30
+ styleCache.getTypeface(styleCache.emphasisFontFamily, resolvedStyle)
31
31
  } else if (styleCache.emphasisFontStyle == "normal") {
32
32
  currentTypeface
33
33
  } else {