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
@@ -0,0 +1,27 @@
1
+ package com.swmansion.enriched.markdown.input.styles
2
+
3
+ import com.swmansion.enriched.markdown.input.model.BlockRange
4
+ import com.swmansion.enriched.markdown.input.model.BlockType
5
+ import com.swmansion.enriched.markdown.input.model.InputFormatterStyle
6
+ import com.swmansion.enriched.markdown.input.spans.InputHeadingSpan
7
+
8
+ /**
9
+ * Block handler for ATX headings (H1-H6). A single instance serves all six levels:
10
+ * it reads the H-level from each [BlockRange.level], so it is registered in the
11
+ * [com.swmansion.enriched.markdown.input.formatting.InputFormatter] under every
12
+ * `HEADING_n` key. The level — not [blockType] — drives styling and serialization,
13
+ * so [blockType] is only a nominal interface value and never consulted by the
14
+ * formatter (it dispatches on `range.type`).
15
+ */
16
+ class HeadingBlockHandler : BlockHandler {
17
+ override val blockType: BlockType = BlockType.HEADING_1
18
+
19
+ override fun createSpans(
20
+ blockRange: BlockRange,
21
+ style: InputFormatterStyle,
22
+ ): List<Any> = listOf(InputHeadingSpan(blockRange.level, style))
23
+
24
+ override fun spanClasses(): List<Class<*>> = listOf(InputHeadingSpan::class.java)
25
+
26
+ override fun markdownLinePrefix(blockRange: BlockRange): String = "#".repeat(blockRange.level.coerceIn(1, 6)) + " "
27
+ }
@@ -0,0 +1,38 @@
1
+ package com.swmansion.enriched.markdown.input.styles
2
+
3
+ import com.swmansion.enriched.markdown.input.model.BlockRange
4
+ import com.swmansion.enriched.markdown.input.model.BlockType
5
+ import com.swmansion.enriched.markdown.input.model.InputFormatterStyle
6
+ import com.swmansion.enriched.markdown.input.spans.InputListItemSpacingSpan
7
+ import com.swmansion.enriched.markdown.input.spans.InputOrderedListMarkerSpan
8
+
9
+ /**
10
+ * Block handler for ordered list items. One instance serves every nesting depth —
11
+ * the 0-based depth rides in [BlockRange.level] and the displayed number in
12
+ * [BlockRange.ordinal], both maintained by the block store.
13
+ */
14
+ class OrderedListBlockHandler(
15
+ private val density: Float,
16
+ ) : BlockHandler {
17
+ override val blockType: BlockType = BlockType.ORDERED_LIST_ITEM
18
+
19
+ override val continuesOnNewline: Boolean = true
20
+
21
+ override fun createSpans(
22
+ blockRange: BlockRange,
23
+ style: InputFormatterStyle,
24
+ ): List<Any> {
25
+ val spans = mutableListOf<Any>(InputOrderedListMarkerSpan(blockRange.level, blockRange.ordinal, density))
26
+ val spacingPx = (style.listItemSpacing * density).toInt()
27
+ if (spacingPx > 0) {
28
+ spans.add(InputListItemSpacingSpan(spacingPx))
29
+ }
30
+ return spans
31
+ }
32
+
33
+ override fun spanClasses(): List<Class<*>> = listOf(InputOrderedListMarkerSpan::class.java, InputListItemSpacingSpan::class.java)
34
+
35
+ /** `"1. "` numbering from the range's ordinal, indented three spaces per nesting depth. */
36
+ override fun markdownLinePrefix(blockRange: BlockRange): String =
37
+ " ".repeat(blockRange.level.coerceAtLeast(0)) + "${blockRange.ordinal}. "
38
+ }
@@ -0,0 +1,37 @@
1
+ package com.swmansion.enriched.markdown.input.styles
2
+
3
+ import com.swmansion.enriched.markdown.input.model.BlockRange
4
+ import com.swmansion.enriched.markdown.input.model.BlockType
5
+ import com.swmansion.enriched.markdown.input.model.InputFormatterStyle
6
+ import com.swmansion.enriched.markdown.input.spans.InputBulletSpan
7
+ import com.swmansion.enriched.markdown.input.spans.InputListItemSpacingSpan
8
+
9
+ /**
10
+ * Block handler for unordered (bullet) list items. One instance serves every
11
+ * nesting depth — the 0-based depth rides in [BlockRange.level] and drives the
12
+ * indent, marker glyph, and serialized indentation.
13
+ */
14
+ class UnorderedListBlockHandler(
15
+ private val density: Float,
16
+ ) : BlockHandler {
17
+ override val blockType: BlockType = BlockType.UNORDERED_LIST_ITEM
18
+
19
+ override val continuesOnNewline: Boolean = true
20
+
21
+ override fun createSpans(
22
+ blockRange: BlockRange,
23
+ style: InputFormatterStyle,
24
+ ): List<Any> {
25
+ val spans = mutableListOf<Any>(InputBulletSpan(blockRange.level, density))
26
+ val spacingPx = (style.listItemSpacing * density).toInt()
27
+ if (spacingPx > 0) {
28
+ spans.add(InputListItemSpacingSpan(spacingPx))
29
+ }
30
+ return spans
31
+ }
32
+
33
+ override fun spanClasses(): List<Class<*>> = listOf(InputBulletSpan::class.java, InputListItemSpacingSpan::class.java)
34
+
35
+ /** `"- "` for a top-level item, indented three spaces per nesting depth (wide enough for ordered markers). */
36
+ override fun markdownLinePrefix(blockRange: BlockRange): String = " ".repeat(blockRange.level.coerceAtLeast(0)) + "- "
37
+ }
@@ -10,8 +10,8 @@ import android.view.MenuItem
10
10
  import android.widget.EditText
11
11
  import androidx.appcompat.app.AlertDialog
12
12
  import com.swmansion.enriched.markdown.input.EnrichedMarkdownTextInputView
13
+ import com.swmansion.enriched.markdown.input.MarkdownClipboard
13
14
  import com.swmansion.enriched.markdown.input.formatting.MarkdownSerializer
14
- import com.swmansion.enriched.markdown.input.model.FormattingRange
15
15
  import com.swmansion.enriched.markdown.input.model.StyleType
16
16
 
17
17
  data class InputSelectionMenuConfig(
@@ -112,7 +112,9 @@ class InputContextMenu(
112
112
  if (customIndex in customItemTexts.indices) {
113
113
  val start = view.selectionStart
114
114
  val end = view.selectionEnd
115
- val selectedText = if (start < end) view.text?.substring(start, end) ?: "" else ""
115
+ // Strip the internal ZWSP anchor so it never reaches JS (a selection can
116
+ // span an empty anchored list line).
117
+ val selectedText = if (start < end) MarkdownSerializer.stripZwsp(view.text?.substring(start, end) ?: "") else ""
116
118
  view.eventEmitter.emitContextMenuItemPress(customItemTexts[customIndex], selectedText, start, end)
117
119
  mode.finish()
118
120
  return true
@@ -171,32 +173,12 @@ class InputContextMenu(
171
173
  .show()
172
174
  }
173
175
 
174
- private fun markdownForSelectedRange(): String? {
175
- val selStart = view.selectionStart
176
- val selEnd = view.selectionEnd
177
- if (selStart >= selEnd) return null
178
-
179
- val editable = view.text ?: return null
180
- val selected = editable.subSequence(selStart, selEnd) as? android.text.Spanned ?: return null
181
-
182
- val clippedRanges = mutableListOf<FormattingRange>()
183
- for (range in view.formattingStore.allRanges) {
184
- if (range.end <= selStart || range.start >= selEnd) continue
185
-
186
- val clippedStart = maxOf(range.start, selStart)
187
- val clippedEnd = minOf(range.end, selEnd)
188
- clippedRanges.add(
189
- FormattingRange(range.type, clippedStart - selStart, clippedEnd - selStart, range.url),
190
- )
191
- }
192
-
193
- return MarkdownSerializer.serialize(selected, clippedRanges, view.mathChipController)
194
- }
195
-
176
+ // Explicit "Copy as Markdown": the user asked for markdown syntax, so it is
177
+ // also the plain-text representation external targets receive.
196
178
  fun copyAsMarkdown() {
197
- val markdown = markdownForSelectedRange() ?: return
179
+ val markdown = view.markdownForSelectedRange() ?: return
198
180
  val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return
199
- clipboard.setPrimaryClip(ClipData.newPlainText("Markdown", markdown))
181
+ clipboard.setPrimaryClip(MarkdownClipboard.newMarkdownClip(markdown, markdown))
200
182
  }
201
183
 
202
184
  companion object {
@@ -37,6 +37,7 @@ data class MarkdownASTNode(
37
37
  Superscript,
38
38
  Subscript,
39
39
  Highlight,
40
+ SoftBreak,
40
41
  }
41
42
 
42
43
  fun getAttribute(key: String): String? = attributes[key]
@@ -10,6 +10,7 @@ data class Md4cFlags(
10
10
  val subscript: Boolean = false,
11
11
  val highlight: Boolean = false,
12
12
  val permissiveAutolinks: Boolean = true,
13
+ val hardSoftBreaks: Boolean = false,
13
14
  ) {
14
15
  companion object {
15
16
  val DEFAULT = Md4cFlags()
@@ -42,6 +42,9 @@ class BlockStyleContext {
42
42
  var listType: ListType? = null
43
43
  var listItemNumber = 0
44
44
  var taskItemCount = 0
45
+ var accumulatedIndent = 0
46
+
47
+ val listItemStarts = mutableListOf<Int>()
45
48
 
46
49
  private val orderedListItemNumbers = ArrayDeque<Int>()
47
50
 
@@ -106,8 +109,10 @@ class BlockStyleContext {
106
109
  listItemNumber++
107
110
  }
108
111
 
109
- fun resetListItemNumber() {
110
- listItemNumber = 0
112
+ fun resetListItemNumber(startNumber: Int = 1) {
113
+ // ListItemRenderer pre-increments, so the counter starts one below the
114
+ // first rendered number.
115
+ listItemNumber = startNumber - 1
111
116
  }
112
117
 
113
118
  fun pushOrderedListItemNumber() {
@@ -147,6 +152,8 @@ class BlockStyleContext {
147
152
  listType = null
148
153
  listItemNumber = 0
149
154
  taskItemCount = 0
155
+ accumulatedIndent = 0
156
+ listItemStarts.clear()
150
157
  orderedListItemNumbers.clear()
151
158
  }
152
159
  }
@@ -1,13 +1,16 @@
1
1
  package com.swmansion.enriched.markdown.renderer
2
2
 
3
+ import android.graphics.Paint
3
4
  import android.text.SpannableStringBuilder
5
+ import android.text.Spanned
6
+ import android.text.style.LineHeightSpan
4
7
  import com.swmansion.enriched.markdown.parser.MarkdownASTNode
5
8
  import com.swmansion.enriched.markdown.spans.BlockquoteSpan
6
9
  import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_CONTAINER_BACKGROUND
7
10
  import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
11
+ import com.swmansion.enriched.markdown.utils.text.span.applyLineHeightSkippingImages
8
12
  import com.swmansion.enriched.markdown.utils.text.span.applyMarginBottom
9
13
  import com.swmansion.enriched.markdown.utils.text.span.applyMarginTop
10
- import com.swmansion.enriched.markdown.utils.text.span.createLineHeightSpan
11
14
 
12
15
  class BlockquoteRenderer(
13
16
  private val config: RendererConfig,
@@ -19,6 +22,10 @@ class BlockquoteRenderer(
19
22
  onLinkLongPress: ((String) -> Unit)?,
20
23
  factory: RendererFactory,
21
24
  ) {
25
+ if (builder.isNotEmpty() && builder.last() != '\n') {
26
+ builder.append("\n")
27
+ }
28
+
22
29
  val start = builder.length
23
30
  val style = config.style.blockquoteStyle
24
31
  val context = factory.blockStyleContext
@@ -36,19 +43,31 @@ class BlockquoteRenderer(
36
43
  }
37
44
 
38
45
  if (builder.length == start) return
39
- val end = builder.length
46
+ var end = builder.length
47
+ val padding = style.padding.toInt()
48
+
49
+ var contentStart = start
50
+ if (padding > 0) {
51
+ builder.insert(start, "\n")
52
+ builder.setSpan(
53
+ BlockquotePaddingSpacerSpan(padding),
54
+ start,
55
+ start + 1,
56
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
57
+ )
58
+ contentStart = start + 1
59
+ end += 1
60
+ }
40
61
 
41
62
  // Find immediately nested quotes to exclude them from this level's line-height/margins
42
63
  val nestedRanges =
43
64
  builder
44
- .getSpans(start, end, BlockquoteSpan::class.java)
65
+ .getSpans(contentStart, end, BlockquoteSpan::class.java)
45
66
  .filter { it.depth == depth + 1 }
46
67
  .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
47
68
  .sortedBy { it.first }
48
69
 
49
- // The accent bar span covers the full range for visual continuity.
50
- // SPAN_FLAGS_CONTAINER_BACKGROUND keeps the blockquote fill under any
51
- // inline chip/pill backgrounds on the same line.
70
+ // The accent bar / background span covers the full box, incl. the top spacer.
52
71
  builder.setSpan(
53
72
  BlockquoteSpan(style, depth, factory.context, factory.styleCache),
54
73
  start,
@@ -56,8 +75,16 @@ class BlockquoteRenderer(
56
75
  SPAN_FLAGS_CONTAINER_BACKGROUND,
57
76
  )
58
77
 
59
- // Apply styling only to segments that are NOT nested quotes
60
- applySpansExcludingNested(builder, nestedRanges, start, end, createLineHeightSpan(style.lineHeight))
78
+ applyLineHeightExcludingNested(builder, nestedRanges, contentStart, end, style.lineHeight)
79
+
80
+ if (padding > 0) {
81
+ builder.setSpan(
82
+ BlockquoteBoundaryPaddingSpan(padding),
83
+ contentStart,
84
+ end,
85
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
86
+ )
87
+ }
61
88
 
62
89
  // Margins are only applied by the outermost (root) quote
63
90
  if (depth == 0) {
@@ -66,22 +93,83 @@ class BlockquoteRenderer(
66
93
  }
67
94
  }
68
95
 
69
- private fun applySpansExcludingNested(
96
+ /**
97
+ * A blockquote's top padding, rendered as a standalone spacer line of exactly
98
+ * [padding] px.
99
+ *
100
+ * Top padding cannot be added by growing the first content line's ascent:
101
+ * StaticLayout reuses one FontMetricsInt across a paragraph's soft-wrapped lines,
102
+ * so the inflated first-line metrics leak into the wrapped continuation line, where
103
+ * the line-height clamp then skews them into overlapping baselines. A spacer sits in
104
+ * its own paragraph, and setting absolute metrics keeps it fixed regardless of that
105
+ * reuse.
106
+ */
107
+ private class BlockquotePaddingSpacerSpan(
108
+ private val padding: Int,
109
+ ) : LineHeightSpan {
110
+ override fun chooseHeight(
111
+ text: CharSequence,
112
+ startLine: Int,
113
+ endLine: Int,
114
+ spanstartv: Int,
115
+ v: Int,
116
+ fm: Paint.FontMetricsInt,
117
+ ) {
118
+ fm.top = 0
119
+ fm.ascent = 0
120
+ fm.descent = padding
121
+ fm.bottom = padding
122
+ }
123
+ }
124
+
125
+ /**
126
+ * A blockquote's bottom padding, grown onto the last content line's descent (safe
127
+ * because no later line inherits its metrics; top padding uses
128
+ * [BlockquotePaddingSpacerSpan]).
129
+ *
130
+ * The span range can include trailing '\n'(s) that form no visible line, so the last
131
+ * drawn line ends before getSpanEnd; the comparison uses the visible content end.
132
+ */
133
+ private class BlockquoteBoundaryPaddingSpan(
134
+ private val padding: Int,
135
+ ) : LineHeightSpan {
136
+ override fun chooseHeight(
137
+ text: CharSequence,
138
+ startLine: Int,
139
+ endLine: Int,
140
+ spanstartv: Int,
141
+ v: Int,
142
+ fm: Paint.FontMetricsInt,
143
+ ) {
144
+ if (text !is Spanned) return
145
+
146
+ var contentEnd = text.getSpanEnd(this)
147
+ while (contentEnd > 0 && text[contentEnd - 1] == '\n') {
148
+ contentEnd--
149
+ }
150
+ if (endLine >= contentEnd) {
151
+ fm.descent += padding
152
+ fm.bottom += padding
153
+ }
154
+ }
155
+ }
156
+
157
+ private fun applyLineHeightExcludingNested(
70
158
  builder: SpannableStringBuilder,
71
159
  nestedRanges: List<Pair<Int, Int>>,
72
160
  start: Int,
73
161
  end: Int,
74
- span: Any,
162
+ lineHeight: Float,
75
163
  ) {
76
164
  var currentPos = start
77
165
  for ((nestedStart, nestedEnd) in nestedRanges) {
78
166
  if (currentPos < nestedStart) {
79
- builder.setSpan(span, currentPos, nestedStart, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
167
+ applyLineHeightSkippingImages(builder, currentPos, nestedStart, lineHeight)
80
168
  }
81
169
  currentPos = nestedEnd
82
170
  }
83
171
  if (currentPos < end) {
84
- builder.setSpan(span, currentPos, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
172
+ applyLineHeightSkippingImages(builder, currentPos, end, lineHeight)
85
173
  }
86
174
  }
87
175
  }
@@ -7,6 +7,8 @@ import android.text.style.LineHeightSpan
7
7
  import com.swmansion.enriched.markdown.parser.MarkdownASTNode
8
8
  import com.swmansion.enriched.markdown.spans.CodeBlockSpan
9
9
  import com.swmansion.enriched.markdown.spans.MarginBottomSpan
10
+ import com.swmansion.enriched.markdown.utils.common.CodeBlockHighlighter
11
+ import com.swmansion.enriched.markdown.utils.common.CodeBlockNode
10
12
  import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
11
13
  import com.swmansion.enriched.markdown.utils.text.span.applyMarginTop
12
14
 
@@ -20,6 +22,10 @@ class CodeBlockRenderer(
20
22
  onLinkLongPress: ((String) -> Unit)?,
21
23
  factory: RendererFactory,
22
24
  ) {
25
+ if (builder.isNotEmpty() && builder.last() != '\n') {
26
+ builder.append("\n")
27
+ }
28
+
23
29
  val start = builder.length
24
30
  val style = config.style.codeBlockStyle
25
31
  val context = factory.blockStyleContext
@@ -40,11 +46,20 @@ class CodeBlockRenderer(
40
46
  if (builder.length == contentStart) return
41
47
 
42
48
  val end = builder.length
49
+
50
+ CodeBlockHighlighter.highlight(
51
+ builder,
52
+ CodeBlockNode.extractCode(node),
53
+ CodeBlockNode.language(node),
54
+ style,
55
+ contentStart,
56
+ )
57
+
43
58
  val padding = style.padding.toInt()
44
59
 
45
60
  // Apply background, borders, and horizontal padding to content only
46
61
  builder.setSpan(
47
- CodeBlockSpan(style, factory.context, factory.styleCache),
62
+ CodeBlockSpan(style, factory.context, factory.styleCache, context.accumulatedIndent),
48
63
  contentStart,
49
64
  end,
50
65
  SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
@@ -19,6 +19,10 @@ class HeadingRenderer(
19
19
  onLinkLongPress: ((String) -> Unit)?,
20
20
  factory: RendererFactory,
21
21
  ) {
22
+ if (builder.isNotEmpty() && builder.last() != '\n') {
23
+ builder.append("\n")
24
+ }
25
+
22
26
  val level = node.getAttribute("level")?.toIntOrNull() ?: 1
23
27
  val start = builder.length
24
28
 
@@ -36,6 +36,7 @@ class ListContextManager(
36
36
  fun enterList(
37
37
  listType: BlockStyleContext.ListType,
38
38
  style: ListStyle,
39
+ startNumber: Int = 1,
39
40
  ): ListEntryState {
40
41
  val previousDepth = context.listDepth
41
42
  val isNested = previousDepth > 0
@@ -58,7 +59,7 @@ class ListContextManager(
58
59
  context.setUnorderedListStyle(style)
59
60
  }
60
61
  }
61
- context.resetListItemNumber()
62
+ context.resetListItemNumber(startNumber)
62
63
 
63
64
  return ListEntryState(
64
65
  previousDepth = previousDepth,
@@ -5,6 +5,8 @@ import android.text.style.ForegroundColorSpan
5
5
  import android.text.style.StrikethroughSpan
6
6
  import com.swmansion.enriched.markdown.parser.MarkdownASTNode
7
7
  import com.swmansion.enriched.markdown.spans.BaseListSpan
8
+ import com.swmansion.enriched.markdown.spans.CodeBlockSpan
9
+ import com.swmansion.enriched.markdown.spans.ListMarkerAnchorSpan
8
10
  import com.swmansion.enriched.markdown.spans.OrderedListSpan
9
11
  import com.swmansion.enriched.markdown.spans.TaskListSpan
10
12
  import com.swmansion.enriched.markdown.spans.UnorderedListSpan
@@ -33,19 +35,10 @@ class ListItemRenderer(
33
35
 
34
36
  val taskIndex = if (isTask) styleContext.taskItemCount++ else -1
35
37
 
36
- factory.renderChildren(node, builder, onLinkPress, onLinkLongPress)
37
-
38
- if (builder.length == start || builder.substring(start).isBlank()) return
39
-
40
- while (builder.length > start && builder.last() == '\n') {
41
- builder.delete(builder.length - 1, builder.length)
42
- }
43
- builder.append("\n")
44
-
45
38
  val depth = styleContext.listDepth - 1
46
39
  val listStyle = config.style.listStyle
47
- val itemEnd = builder.length
48
- val span =
40
+
41
+ fun makeSpan(drawsMarker: Boolean): BaseListSpan =
49
42
  if (isTask) {
50
43
  TaskListSpan(
51
44
  taskStyle = config.style.taskListStyle,
@@ -55,22 +48,85 @@ class ListItemRenderer(
55
48
  styleCache = factory.styleCache,
56
49
  taskIndex = taskIndex,
57
50
  isChecked = isChecked,
51
+ drawsMarker = drawsMarker,
58
52
  )
59
53
  } else {
60
54
  when (listType) {
61
55
  BlockStyleContext.ListType.UNORDERED -> {
62
- UnorderedListSpan(listStyle, depth, factory.context, factory.styleCache)
56
+ UnorderedListSpan(listStyle, depth, factory.context, factory.styleCache, drawsMarker)
63
57
  }
64
58
 
65
59
  BlockStyleContext.ListType.ORDERED -> {
66
- OrderedListSpan(listStyle, depth, factory.context, factory.styleCache).apply {
60
+ OrderedListSpan(listStyle, depth, factory.context, factory.styleCache, drawsMarker).apply {
67
61
  setItemNumber(styleContext.listItemNumber)
68
62
  }
69
63
  }
70
64
  }
71
65
  }
72
66
 
73
- builder.setSpan(span, start, itemEnd, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
67
+ val markerSpan = makeSpan(drawsMarker = true)
68
+
69
+ val prevIndent = styleContext.accumulatedIndent
70
+ styleContext.accumulatedIndent = prevIndent + markerSpan.getLeadingMargin(false)
71
+ try {
72
+ factory.renderChildren(node, builder, onLinkPress, onLinkLongPress)
73
+ } finally {
74
+ styleContext.accumulatedIndent = prevIndent
75
+ }
76
+
77
+ if (builder.length == start || builder.substring(start).isBlank()) return
78
+
79
+ styleContext.listItemStarts.add(start)
80
+
81
+ if (builder.last() != '\n') {
82
+ builder.append("\n")
83
+ }
84
+
85
+ val itemEnd = builder.length
86
+
87
+ // Anchor the marker on the first content character so items that open with
88
+ // a code block or nested sublist still get their marker.
89
+ var anchor = start
90
+ while (anchor < itemEnd && builder[anchor].isWhitespace()) anchor++
91
+ if (anchor == itemEnd) anchor = start
92
+
93
+ val plainAnchor =
94
+ builder.getSpans(anchor, anchor + 1, CodeBlockSpan::class.java).isEmpty() &&
95
+ builder.getSpans(anchor, anchor + 1, BaseListSpan::class.java).none { it.depth > depth }
96
+
97
+ val codeBlockRanges =
98
+ builder
99
+ .getSpans(start, itemEnd, CodeBlockSpan::class.java)
100
+ .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
101
+ .filter { it.first < it.second }
102
+ .sortedBy { it.first }
103
+
104
+ var pos = start
105
+ var isFirstSegment = true
106
+ for ((cbStart, cbEnd) in codeBlockRanges) {
107
+ if (pos < cbStart) {
108
+ builder.setSpan(
109
+ if (isFirstSegment && plainAnchor) markerSpan else makeSpan(drawsMarker = false),
110
+ pos,
111
+ cbStart,
112
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
113
+ )
114
+ isFirstSegment = false
115
+ }
116
+ pos = maxOf(pos, cbEnd)
117
+ }
118
+ if (pos < itemEnd) {
119
+ builder.setSpan(
120
+ if (isFirstSegment && plainAnchor) markerSpan else makeSpan(drawsMarker = false),
121
+ pos,
122
+ itemEnd,
123
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
124
+ )
125
+ }
126
+
127
+ if (!plainAnchor) {
128
+ builder.setSpan(ListMarkerAnchorSpan(markerSpan), anchor, anchor + 1, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
129
+ }
74
130
 
75
131
  if (isTask && isChecked) {
76
132
  applyCheckedDecorations(builder, start, itemEnd, depth)
@@ -90,10 +146,10 @@ class ListItemRenderer(
90
146
  if (checkedTextColor == 0 && !strikethrough) return
91
147
 
92
148
  val excludedRanges =
93
- builder
94
- .getSpans(itemStart, itemEnd, BaseListSpan::class.java)
95
- .filter { it.depth > itemDepth }
96
- .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
149
+ (
150
+ builder.getSpans(itemStart, itemEnd, BaseListSpan::class.java).filter { it.depth > itemDepth } +
151
+ builder.getSpans(itemStart, itemEnd, CodeBlockSpan::class.java).toList()
152
+ ).map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
97
153
  .sortedBy { it.first }
98
154
 
99
155
  var currentPos = itemStart
@@ -2,7 +2,9 @@ package com.swmansion.enriched.markdown.renderer
2
2
 
3
3
  import android.text.SpannableStringBuilder
4
4
  import com.swmansion.enriched.markdown.parser.MarkdownASTNode
5
+ import com.swmansion.enriched.markdown.spans.ListItemSpacingSpan
5
6
  import com.swmansion.enriched.markdown.spans.MarginBottomSpan
7
+ import com.swmansion.enriched.markdown.styles.ListStyle
6
8
  import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
7
9
  import com.swmansion.enriched.markdown.utils.text.span.applyLineHeightSkippingImages
8
10
  import com.swmansion.enriched.markdown.utils.text.span.applyMarginTop
@@ -22,8 +24,15 @@ class ListRenderer(
22
24
  val listStyle = config.style.listStyle
23
25
  val listType = if (isOrdered) BlockStyleContext.ListType.ORDERED else BlockStyleContext.ListType.UNORDERED
24
26
 
27
+ val startNumber =
28
+ if (isOrdered) {
29
+ node.getAttribute("start")?.toIntOrNull()?.coerceAtLeast(0) ?: 1
30
+ } else {
31
+ 1
32
+ }
33
+
25
34
  val contextManager = ListContextManager(factory.blockStyleContext)
26
- val entryState = contextManager.enterList(listType, listStyle)
35
+ val entryState = contextManager.enterList(listType, listStyle, startNumber)
27
36
 
28
37
  // For top-level lists, insert marginTop spacer before rendering content
29
38
  if (entryState.previousDepth == 0) {
@@ -45,7 +54,7 @@ class ListRenderer(
45
54
  }
46
55
 
47
56
  if (builder.length > contentStart) {
48
- applyListSpacing(builder, contentStart, entryState.previousDepth, listStyle)
57
+ applyListSpacing(builder, contentStart, entryState.previousDepth, listStyle, factory.blockStyleContext)
49
58
  }
50
59
  }
51
60
 
@@ -53,15 +62,18 @@ class ListRenderer(
53
62
  builder: SpannableStringBuilder,
54
63
  start: Int,
55
64
  depth: Int,
56
- style: com.swmansion.enriched.markdown.styles.BaseBlockStyle,
65
+ style: ListStyle,
66
+ styleContext: BlockStyleContext,
57
67
  ) {
58
68
  // TODO: LineHeightSpan may also clip superscript/subscript glyphs that extend
59
69
  // outside the normal line bounds. A similar "skip" strategy as for images may
60
70
  // be needed here once super/subscript usage in practice is better understood.
61
71
  applyLineHeightSkippingImages(builder, start, builder.length, style.lineHeight)
62
72
 
63
- // External bottom margin is only handled by the root-level list
73
+ // Item spacing and external bottom margin are only handled by the root-level list
64
74
  if (depth == 0) {
75
+ applyItemSpacing(builder, start, style.itemSpacing, styleContext)
76
+
65
77
  builder.append("\n")
66
78
  builder.setSpan(
67
79
  MarginBottomSpan(style.marginBottom),
@@ -71,4 +83,31 @@ class ListRenderer(
71
83
  )
72
84
  }
73
85
  }
86
+
87
+ // Inserts the vertical gap between consecutive items (including nested ones)
88
+ // by spanning the newline that precedes each item start. The first item of
89
+ // the root list gets no spacing above it.
90
+ private fun applyItemSpacing(
91
+ builder: SpannableStringBuilder,
92
+ start: Int,
93
+ itemSpacing: Float,
94
+ styleContext: BlockStyleContext,
95
+ ) {
96
+ if (itemSpacing <= 0f) return
97
+
98
+ val itemStarts =
99
+ styleContext.listItemStarts
100
+ .filter { it in start until builder.length }
101
+ .distinct()
102
+ .sorted()
103
+
104
+ for (itemStart in itemStarts.drop(1)) {
105
+ builder.setSpan(
106
+ ListItemSpacingSpan(itemSpacing),
107
+ itemStart - 1,
108
+ itemStart,
109
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
110
+ )
111
+ }
112
+ }
74
113
  }