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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/README.md +54 -8
  2. package/ReactNativeEnrichedMarkdown.podspec +82 -73
  3. package/android/build.gradle +191 -1
  4. package/android/consumer-rules.pro +1 -1
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +9 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +3 -0
  7. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +27 -0
  8. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +9 -0
  9. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +9 -0
  10. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +3 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +40 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +839 -23
  15. package/android/src/main/baseline-prof.txt +1 -1
  16. package/android/src/main/cpp/jni-adapter.cpp +74 -0
  17. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +175 -11
  18. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +17 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +50 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +48 -1
  21. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextLayoutManager.kt +10 -1
  22. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +42 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +82 -4
  24. package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyPressEvent.kt +24 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +62 -4
  26. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +911 -460
  27. package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
  30. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +101 -7
  34. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
  36. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
  37. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
  39. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockSyntaxSentinel.kt +69 -0
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +180 -27
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +7 -2
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  65. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  66. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  68. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  69. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  77. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +30 -27
  78. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  79. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  80. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +27 -3
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  90. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  91. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  93. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  94. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  95. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  96. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  97. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  100. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  101. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +33 -7
  102. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  103. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  104. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  105. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  106. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +20 -0
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  117. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +19 -2
  118. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +388 -0
  119. package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
  120. package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
  121. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +32 -5
  122. package/android/src/main/jni/CMakeLists.txt +53 -0
  123. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
  124. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
  125. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
  126. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
  127. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  128. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
  129. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
  130. package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
  131. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +7 -2
  132. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  133. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +2 -0
  134. package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
  135. package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
  136. package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
  137. package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
  138. package/cpp/highlight/HighlightGrammars.hpp +28 -0
  139. package/cpp/highlight/code_highlight_podspec.rb +211 -0
  140. package/cpp/highlight/gen-registry.mjs +155 -0
  141. package/cpp/highlight/grammar-versions.json +159 -0
  142. package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
  143. package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
  144. package/cpp/parser/MD4CParser.cpp +173 -61
  145. package/cpp/parser/MD4CParser.hpp +1 -0
  146. package/cpp/parser/MarkdownASTNode.hpp +2 -1
  147. package/cpp/wasm/ASTSerializer.cpp +2 -0
  148. package/cpp/wasm/build.sh +1 -0
  149. package/cpp/wasm/md4c_wasm.cpp +3 -1
  150. package/docs/ACCESSIBILITY.md +14 -2
  151. package/docs/API_REFERENCE.md +231 -8
  152. package/docs/BREAKING_CHANGES.md +129 -0
  153. package/docs/CODE_HIGHLIGHT.md +133 -0
  154. package/docs/COPY_OPTIONS.md +21 -3
  155. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  156. package/docs/IMAGE_CACHING.md +30 -0
  157. package/docs/INPUT.md +146 -5
  158. package/docs/LATEX_MATH.md +87 -45
  159. package/docs/MACOS.md +1 -1
  160. package/docs/MARKDOWN_STREAMING.md +27 -1
  161. package/docs/NATIVE_ASSETS.md +127 -0
  162. package/docs/RTL.md +1 -1
  163. package/docs/STYLES.md +46 -6
  164. package/docs/TESTING.md +112 -0
  165. package/docs/TEXT.md +16 -6
  166. package/docs/WEB.md +3 -1
  167. package/ios/EnrichedMarkdown.h +7 -0
  168. package/ios/EnrichedMarkdown.mm +262 -67
  169. package/ios/EnrichedMarkdownText.h +7 -0
  170. package/ios/EnrichedMarkdownText.mm +97 -68
  171. package/ios/attachments/ENRMImageAttachment.h +9 -0
  172. package/ios/attachments/ENRMImageAttachment.m +143 -40
  173. package/ios/attachments/ENRMMathInlineAttachment.h +2 -0
  174. package/ios/attachments/ENRMMathInlineAttachment.m +48 -81
  175. package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
  176. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +75 -0
  177. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +63 -0
  178. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +40 -0
  179. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +839 -23
  180. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +131 -0
  181. package/ios/input/ENRMAutoLinkDetector.h +2 -1
  182. package/ios/input/ENRMAutoLinkDetector.mm +79 -41
  183. package/ios/input/ENRMBlockEditCoordinator.h +71 -0
  184. package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
  185. package/ios/input/ENRMBlockRange.h +28 -0
  186. package/ios/input/ENRMBlockRange.mm +30 -0
  187. package/ios/input/ENRMBlockStore.h +56 -0
  188. package/ios/input/ENRMBlockStore.mm +275 -0
  189. package/ios/input/ENRMBlockSyntaxSentinel.h +25 -0
  190. package/ios/input/ENRMBlockSyntaxSentinel.mm +80 -0
  191. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  192. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  193. package/ios/input/ENRMDetectorPipeline.h +2 -1
  194. package/ios/input/ENRMEditPipeline.h +76 -0
  195. package/ios/input/ENRMEditPipeline.mm +228 -0
  196. package/ios/input/ENRMEditSession.h +36 -0
  197. package/ios/input/ENRMEditSession.mm +63 -0
  198. package/ios/input/ENRMFormattingStore.h +17 -0
  199. package/ios/input/ENRMFormattingStore.mm +95 -105
  200. package/ios/input/ENRMInputBlockType.h +91 -0
  201. package/ios/input/ENRMInputBlockType.mm +5 -0
  202. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  203. package/ios/input/ENRMInputEventEmitter.h +56 -0
  204. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  205. package/ios/input/ENRMInputFormatter.h +29 -69
  206. package/ios/input/ENRMInputFormatter.mm +216 -124
  207. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  208. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  209. package/ios/input/ENRMInputLayoutManager.h +9 -0
  210. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  211. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  212. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  213. package/ios/input/ENRMInputParser.h +10 -6
  214. package/ios/input/ENRMInputParser.mm +527 -171
  215. package/ios/input/ENRMInputRemend.mm +2 -2
  216. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  217. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  218. package/ios/input/ENRMInputTextView.mm +103 -8
  219. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  220. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  221. package/ios/input/ENRMLinkCoordinator.h +41 -0
  222. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  223. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  224. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  225. package/ios/input/ENRMMathAtom.h +26 -0
  226. package/ios/input/ENRMMathAtom.mm +12 -0
  227. package/ios/input/ENRMMentionCoordinator.h +45 -0
  228. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  229. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  230. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  231. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  232. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  233. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  234. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  235. package/ios/input/EnrichedMarkdownTextInput.mm +1104 -1024
  236. package/ios/input/InputStylePropsUtils.h +109 -68
  237. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  238. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  239. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  240. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  241. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  242. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  243. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  244. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  245. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  246. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  247. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  248. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  249. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  250. package/ios/internals/MeasurementCache.h +33 -7
  251. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  252. package/ios/math/ENRMMathChipController.h +74 -0
  253. package/ios/math/ENRMMathChipController.mm +476 -0
  254. package/ios/math/ENRMRaTeXBridge.swift +4 -1
  255. package/ios/parser/ENRMMarkdownParser.h +1 -8
  256. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  257. package/ios/parser/MarkdownASTNode.h +2 -1
  258. package/ios/parser/MarkdownParserBridge.mm +4 -0
  259. package/ios/renderer/AttributedRenderer.m +20 -2
  260. package/ios/renderer/BaseRenderer.h +17 -2
  261. package/ios/renderer/BaseRenderer.m +30 -0
  262. package/ios/renderer/BlockquoteRenderer.h +1 -1
  263. package/ios/renderer/BlockquoteRenderer.m +144 -35
  264. package/ios/renderer/CodeBlockRenderer.h +1 -1
  265. package/ios/renderer/CodeBlockRenderer.m +27 -23
  266. package/ios/renderer/CodeRenderer.h +1 -1
  267. package/ios/renderer/CodeRenderer.m +3 -1
  268. package/ios/renderer/ENRMImageRenderer.h +1 -1
  269. package/ios/renderer/ENRMImageRenderer.m +5 -1
  270. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  271. package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
  272. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  273. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  274. package/ios/renderer/EmphasisRenderer.h +1 -1
  275. package/ios/renderer/EmphasisRenderer.m +3 -1
  276. package/ios/renderer/HeadingRenderer.h +1 -1
  277. package/ios/renderer/HeadingRenderer.m +13 -6
  278. package/ios/renderer/HighlightRenderer.h +1 -1
  279. package/ios/renderer/HighlightRenderer.m +3 -1
  280. package/ios/renderer/LinkRenderer.h +1 -1
  281. package/ios/renderer/LinkRenderer.m +3 -1
  282. package/ios/renderer/ListItemRenderer.h +13 -1
  283. package/ios/renderer/ListItemRenderer.m +102 -44
  284. package/ios/renderer/ListRenderer.h +2 -2
  285. package/ios/renderer/ListRenderer.m +63 -25
  286. package/ios/renderer/ParagraphRenderer.h +1 -1
  287. package/ios/renderer/ParagraphRenderer.m +16 -8
  288. package/ios/renderer/RenderContext.h +10 -0
  289. package/ios/renderer/RenderContext.m +44 -0
  290. package/ios/renderer/RendererFactory.m +14 -0
  291. package/ios/renderer/StrikethroughRenderer.h +1 -1
  292. package/ios/renderer/StrikethroughRenderer.m +3 -1
  293. package/ios/renderer/StrongRenderer.h +1 -1
  294. package/ios/renderer/StrongRenderer.m +3 -1
  295. package/ios/renderer/SubscriptRenderer.h +1 -1
  296. package/ios/renderer/SubscriptRenderer.m +3 -1
  297. package/ios/renderer/SuperscriptRenderer.h +1 -1
  298. package/ios/renderer/SuperscriptRenderer.m +3 -1
  299. package/ios/renderer/TextRenderer.h +2 -2
  300. package/ios/renderer/TextRenderer.m +3 -1
  301. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  302. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  303. package/ios/renderer/UnderlineRenderer.h +1 -1
  304. package/ios/renderer/UnderlineRenderer.m +3 -1
  305. package/ios/styles/StyleConfig.h +22 -0
  306. package/ios/styles/StyleConfig.mm +132 -0
  307. package/ios/utils/BlockquoteBorder.h +6 -0
  308. package/ios/utils/BlockquoteBorder.m +249 -52
  309. package/ios/utils/CodeBlockBackground.m +16 -2
  310. package/ios/utils/ENRMAtomicSize.h +65 -0
  311. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  312. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  313. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  314. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  315. package/ios/utils/ENRMFeatureFlags.h +7 -0
  316. package/ios/utils/ENRMImageDownloader.h +11 -1
  317. package/ios/utils/ENRMImageDownloader.m +60 -16
  318. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  319. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  320. package/ios/utils/ENRMMathFallback.h +26 -0
  321. package/ios/utils/ENRMMathFallback.m +18 -0
  322. package/ios/utils/ENRMTextViewSetup.h +35 -17
  323. package/ios/utils/EditMenuUtils.m +22 -1
  324. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  325. package/ios/utils/LastElementUtils.h +1 -0
  326. package/ios/utils/ListMarkerDrawer.m +29 -42
  327. package/ios/utils/MarkdownASTSerializer.m +4 -0
  328. package/ios/utils/ParagraphStyleUtils.h +5 -0
  329. package/ios/utils/ParagraphStyleUtils.m +77 -17
  330. package/ios/utils/RenderedMarkdownSegment.h +13 -1
  331. package/ios/utils/RenderedMarkdownSegment.m +20 -0
  332. package/ios/utils/SegmentRenderer.m +17 -4
  333. package/ios/utils/StreamingMarkdownFilter.h +11 -1
  334. package/ios/utils/StreamingMarkdownFilter.m +98 -2
  335. package/ios/utils/StylePropsUtils.h +63 -0
  336. package/ios/utils/TaskListTapUtils.m +24 -0
  337. package/ios/views/ENRMCodeBlockContainerView.h +46 -0
  338. package/ios/views/ENRMCodeBlockContainerView.m +566 -0
  339. package/ios/views/ENRMMathContainerView.h +12 -0
  340. package/ios/views/ENRMMathContainerView.m +71 -1
  341. package/ios/views/TableContainerView.h +18 -0
  342. package/ios/views/TableContainerView.m +259 -159
  343. package/jest.d.ts +1 -0
  344. package/jest.js +5 -0
  345. package/lib/module/EnrichedMarkdownNativeComponent.ts +65 -0
  346. package/lib/module/EnrichedMarkdownTextInput.js +86 -5
  347. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  348. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +89 -5
  349. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +66 -0
  350. package/lib/module/headingDefaults.js +34 -0
  351. package/lib/module/headingDefaults.js.map +1 -0
  352. package/lib/module/index.js.map +1 -1
  353. package/lib/module/jest/index.js +191 -0
  354. package/lib/module/jest/index.js.map +1 -0
  355. package/lib/module/native/EnrichedMarkdownText.js +30 -4
  356. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  357. package/lib/module/normalizeMarkdownStyle.js +81 -19
  358. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  359. package/lib/module/normalizeMarkdownStyle.web.js +56 -18
  360. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  361. package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
  362. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  363. package/lib/module/normalizeMenuItem.js +1 -1
  364. package/lib/module/utils/textInputState.js +26 -0
  365. package/lib/module/utils/textInputState.js.map +1 -0
  366. package/lib/module/web/EnrichedMarkdownText.js +9 -5
  367. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  368. package/lib/module/web/parseMarkdown.js +4 -3
  369. package/lib/module/web/parseMarkdown.js.map +1 -1
  370. package/lib/module/web/renderers/InlineRenderers.js +6 -0
  371. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  372. package/lib/module/web/renderers/ListRenderers.js +7 -2
  373. package/lib/module/web/renderers/ListRenderers.js.map +1 -1
  374. package/lib/module/web/renderers/index.js +7 -4
  375. package/lib/module/web/renderers/index.js.map +1 -1
  376. package/lib/module/web/styles.js +83 -6
  377. package/lib/module/web/styles.js.map +1 -1
  378. package/lib/module/web/utils.js +1 -0
  379. package/lib/module/web/utils.js.map +1 -1
  380. package/lib/module/web/wasm/md4c.js +0 -0
  381. package/lib/module/web/wasm/md4c.js.map +1 -1
  382. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +62 -0
  383. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  384. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +95 -9
  385. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  386. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +80 -5
  387. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  388. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +62 -0
  389. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  390. package/lib/typescript/src/headingDefaults.d.ts +9 -0
  391. package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
  392. package/lib/typescript/src/index.d.ts +2 -2
  393. package/lib/typescript/src/index.d.ts.map +1 -1
  394. package/lib/typescript/src/index.web.d.ts +1 -1
  395. package/lib/typescript/src/index.web.d.ts.map +1 -1
  396. package/lib/typescript/src/jest/index.d.ts +6 -0
  397. package/lib/typescript/src/jest/index.d.ts.map +1 -0
  398. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
  399. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  400. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  401. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  402. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
  403. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  404. package/lib/typescript/src/types/MarkdownStyle.d.ts +72 -0
  405. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  406. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +27 -0
  407. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  408. package/lib/typescript/src/types/MarkdownTextProps.d.ts +65 -1
  409. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  410. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +19 -0
  411. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  412. package/lib/typescript/src/types/events.d.ts +19 -0
  413. package/lib/typescript/src/types/events.d.ts.map +1 -1
  414. package/lib/typescript/src/utils/textInputState.d.ts +24 -0
  415. package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
  416. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  417. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  418. package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
  419. package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
  420. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  421. package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
  422. package/lib/typescript/src/web/renderers/index.d.ts +2 -1
  423. package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
  424. package/lib/typescript/src/web/styles.d.ts +2 -1
  425. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  426. package/lib/typescript/src/web/types.d.ts +6 -2
  427. package/lib/typescript/src/web/types.d.ts.map +1 -1
  428. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  429. package/package.json +36 -9
  430. package/postinstall.mjs +131 -0
  431. package/ratex-version.json +21 -0
  432. package/src/EnrichedMarkdownNativeComponent.ts +65 -0
  433. package/src/EnrichedMarkdownTextInput.tsx +162 -8
  434. package/src/EnrichedMarkdownTextInputNativeComponent.ts +89 -5
  435. package/src/EnrichedMarkdownTextNativeComponent.ts +66 -0
  436. package/src/headingDefaults.ts +21 -0
  437. package/src/index.tsx +9 -0
  438. package/src/index.web.tsx +1 -0
  439. package/src/jest/index.tsx +215 -0
  440. package/src/native/EnrichedMarkdownText.tsx +40 -2
  441. package/src/normalizeMarkdownStyle.ts +102 -16
  442. package/src/normalizeMarkdownStyle.web.ts +63 -15
  443. package/src/normalizeMarkdownTextInputStyle.ts +110 -35
  444. package/src/normalizeMenuItem.ts +1 -1
  445. package/src/types/MarkdownStyle.ts +74 -0
  446. package/src/types/MarkdownStyleInternal.ts +39 -0
  447. package/src/types/MarkdownTextProps.ts +65 -0
  448. package/src/types/MarkdownTextProps.web.ts +19 -0
  449. package/src/types/events.ts +21 -0
  450. package/src/utils/textInputState.ts +35 -0
  451. package/src/web/EnrichedMarkdownText.tsx +16 -2
  452. package/src/web/parseMarkdown.ts +6 -2
  453. package/src/web/renderers/InlineRenderers.tsx +5 -0
  454. package/src/web/renderers/ListRenderers.tsx +14 -2
  455. package/src/web/renderers/index.tsx +6 -2
  456. package/src/web/styles.ts +89 -5
  457. package/src/web/types.ts +6 -1
  458. package/src/web/utils.ts +1 -0
  459. package/src/web/wasm/md4c.js +0 -0
  460. package/vendor-grammars.mjs +511 -0
  461. package/vendor-ratex.mjs +173 -0
  462. package/app.plugin.js +0 -2
  463. package/plugin/build/withAndroidMath.js +0 -19
  464. package/plugin/build/withIosMath.js +0 -28
  465. package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
  466. package/plugin/src/withAndroidMath.ts +0 -24
  467. package/plugin/src/withIosMath.ts +0 -35
  468. package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
  469. package/plugin/tsconfig.build.json +0 -16
@@ -0,0 +1,388 @@
1
+ package com.swmansion.enriched.markdown.views
2
+
3
+ import android.content.ClipData
4
+ import android.content.ClipboardManager
5
+ import android.content.Context
6
+ import android.graphics.Canvas
7
+ import android.graphics.ColorFilter
8
+ import android.graphics.Paint
9
+ import android.graphics.PixelFormat
10
+ import android.graphics.Typeface
11
+ import android.graphics.drawable.Drawable
12
+ import android.graphics.drawable.GradientDrawable
13
+ import android.os.Build
14
+ import android.text.Layout
15
+ import android.text.SpannableString
16
+ import android.text.StaticLayout
17
+ import android.text.TextPaint
18
+ import android.text.TextUtils
19
+ import android.util.TypedValue
20
+ import android.view.View
21
+ import android.widget.FrameLayout
22
+ import android.widget.HorizontalScrollView
23
+ import android.widget.ImageView
24
+ import androidx.appcompat.widget.AppCompatImageButton
25
+ import androidx.appcompat.widget.AppCompatTextView
26
+ import com.swmansion.enriched.markdown.parser.MarkdownASTNode
27
+ import com.swmansion.enriched.markdown.spans.LineHeightSpan
28
+ import com.swmansion.enriched.markdown.styles.CodeBlockStyle
29
+ import com.swmansion.enriched.markdown.styles.StyleConfig
30
+ import com.swmansion.enriched.markdown.utils.common.CodeBlockHighlighter
31
+ import com.swmansion.enriched.markdown.utils.common.CodeBlockNode
32
+ import com.swmansion.enriched.markdown.utils.text.extensions.applyCodeBlockTextStyle
33
+ import com.swmansion.enriched.markdown.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
34
+ import kotlin.math.ceil
35
+
36
+ /**
37
+ * Block segment view for fenced code blocks (see splitASTIntoSegments):
38
+ * a fixed header bar (language name, copy button) above a horizontally
39
+ * scrolling, non-wrapping code pane. Syntax coloring comes from the
40
+ * CodeBlockHighlighter seam and falls back to plain text. The box visuals
41
+ * must stay in sync with the commonmark flavor's CodeBlockSpan.
42
+ *
43
+ * measureCodeBlockNodeHeight must stay in sync with the view: it builds the
44
+ * same styled text, paint, and header metrics, so the height reported to
45
+ * Yoga matches the laid-out height. Since lines never wrap, the height is
46
+ * independent of the available width.
47
+ */
48
+ class CodeBlockContainerView(
49
+ context: Context,
50
+ styleConfig: StyleConfig,
51
+ ) : FrameLayout(context),
52
+ BlockSegmentView {
53
+ private val codeBlockStyle: CodeBlockStyle = styleConfig.codeBlockStyle
54
+
55
+ private val inset = contentInset(codeBlockStyle)
56
+ private val borderW = borderPx(codeBlockStyle)
57
+ private val headerH = headerHeight(codeBlockStyle)
58
+
59
+ override val segmentMarginTop: Int get() = codeBlockStyle.marginTop.toInt()
60
+ override val segmentMarginBottom: Int get() = codeBlockStyle.marginBottom.toInt()
61
+
62
+ var copyLabel: String = ""
63
+ set(value) {
64
+ field = value
65
+ copyButton.contentDescription = value
66
+ }
67
+ var copyAsMarkdownLabel: String = ""
68
+
69
+ // Hides the long-press copy menu only; the header copy button is a visible
70
+ // control, not a context menu, and stays.
71
+ var contextMenuHidden: Boolean = false
72
+
73
+ var onCopyPress: ((code: String, language: String) -> Unit)? = null
74
+
75
+ private var code: String = ""
76
+ private var language: String? = null
77
+ private var fenceChar: String = "`"
78
+
79
+ // True until the closing fence arrives: highlighting is deferred and copying
80
+ // is disabled, while the header stays visible.
81
+ var pending: Boolean = false
82
+ set(value) {
83
+ if (field == value) return
84
+ field = value
85
+ copyButton.isEnabled = !value
86
+ // The reconciler can reuse an unchanged block without re-applying the
87
+ // node, so re-derive here to pick up highlighting once the block closes.
88
+ rebuildCodeText()
89
+ }
90
+
91
+ private val textView =
92
+ AppCompatTextView(context).apply {
93
+ includeFontPadding = false
94
+ setLineSpacing(0f, 1f)
95
+ breakStrategy = Layout.BREAK_STRATEGY_SIMPLE
96
+ hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
97
+ layoutDirection = View.LAYOUT_DIRECTION_LTR
98
+ textDirection = View.TEXT_DIRECTION_LTR
99
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, codeBlockStyle.fontSize)
100
+ typeface = createCodePaint(codeBlockStyle, context).typeface
101
+ setTextColor(codeBlockStyle.color)
102
+ val horizontalPad = (inset - borderW).coerceAtLeast(0)
103
+ setPadding(horizontalPad, inset, horizontalPad, inset)
104
+ setOnLongClickListener { view -> showContextMenu(view) }
105
+ }
106
+
107
+ private val scrollView =
108
+ HorizontalScrollView(context).apply {
109
+ isHorizontalScrollBarEnabled = true
110
+ overScrollMode = View.OVER_SCROLL_NEVER
111
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
112
+ horizontalScrollbarThumbDrawable =
113
+ GradientDrawable().apply {
114
+ setColor(secondaryColor(codeBlockStyle.color))
115
+ cornerRadius = context.resources.displayMetrics.density * 2
116
+ }
117
+ }
118
+ addView(textView, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
119
+ }
120
+
121
+ private val languageView =
122
+ AppCompatTextView(context).apply {
123
+ includeFontPadding = false
124
+ maxLines = 1
125
+ ellipsize = TextUtils.TruncateAt.END
126
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, codeBlockStyle.fontSize * HEADER_LABEL_SCALE)
127
+ typeface = headerTypeface
128
+ setTextColor(secondaryColor(codeBlockStyle.color))
129
+ }
130
+
131
+ private val copyButton =
132
+ AppCompatImageButton(context).apply {
133
+ background = null
134
+ scaleType = ImageView.ScaleType.CENTER
135
+ setImageDrawable(
136
+ CopyIconDrawable(
137
+ secondaryColor(codeBlockStyle.color),
138
+ ceil(codeBlockStyle.fontSize * HEADER_ICON_SCALE).toInt(),
139
+ ),
140
+ )
141
+ setOnClickListener { copyCode() }
142
+ }
143
+
144
+ private val dividerPaint =
145
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
146
+ color = dividerColor(codeBlockStyle.color)
147
+ strokeWidth = context.resources.displayMetrics.density
148
+ }
149
+
150
+ init {
151
+ setWillNotDraw(false)
152
+ background =
153
+ GradientDrawable().apply {
154
+ setColor(codeBlockStyle.backgroundColor)
155
+ cornerRadius = codeBlockStyle.borderRadius
156
+ if (codeBlockStyle.borderWidth > 0f) {
157
+ setStroke(ceil(codeBlockStyle.borderWidth).toInt(), codeBlockStyle.borderColor)
158
+ }
159
+ }
160
+ isLongClickable = true
161
+ setOnLongClickListener { view -> showContextMenu(view) }
162
+ addView(scrollView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
163
+ addView(languageView, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
164
+ addView(copyButton, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
165
+ }
166
+
167
+ fun applyCodeBlockNode(node: MarkdownASTNode) {
168
+ code = CodeBlockNode.extractCode(node)
169
+ fenceChar = CodeBlockNode.fenceChar(node)
170
+
171
+ val newLanguage = CodeBlockNode.language(node)
172
+ if (newLanguage != language) {
173
+ language = newLanguage
174
+ languageView.text = CodeBlockNode.displayLanguageName(newLanguage)
175
+ }
176
+
177
+ rebuildCodeText()
178
+ }
179
+
180
+ // Highlighting is deferred while pending, applied once the block closes.
181
+ private fun rebuildCodeText() {
182
+ val plainCode = buildCodeText(code, codeBlockStyle)
183
+ if (!pending) {
184
+ CodeBlockHighlighter.highlight(plainCode, code, language, codeBlockStyle)
185
+ }
186
+ textView.text = plainCode
187
+ }
188
+
189
+ override fun onMeasure(
190
+ widthSpec: Int,
191
+ heightSpec: Int,
192
+ ) {
193
+ val measuredWidth = MeasureSpec.getSize(widthSpec)
194
+ scrollView.measure(
195
+ MeasureSpec.makeMeasureSpec((measuredWidth - borderW * 2).coerceAtLeast(0), MeasureSpec.EXACTLY),
196
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
197
+ )
198
+ copyButton.measure(
199
+ MeasureSpec.makeMeasureSpec(headerH, MeasureSpec.EXACTLY),
200
+ MeasureSpec.makeMeasureSpec(headerH, MeasureSpec.EXACTLY),
201
+ )
202
+ val labelMaxWidth = (measuredWidth - inset * 2 - headerH).coerceAtLeast(0)
203
+ languageView.measure(
204
+ MeasureSpec.makeMeasureSpec(labelMaxWidth, MeasureSpec.AT_MOST),
205
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
206
+ )
207
+ setMeasuredDimension(measuredWidth, headerH + scrollView.measuredHeight)
208
+ }
209
+
210
+ override fun onLayout(
211
+ changed: Boolean,
212
+ left: Int,
213
+ top: Int,
214
+ right: Int,
215
+ bottom: Int,
216
+ ) {
217
+ val width = right - left
218
+ val labelTop = headerH - languageView.measuredHeight
219
+ languageView.layout(
220
+ inset,
221
+ labelTop,
222
+ inset + languageView.measuredWidth,
223
+ labelTop + languageView.measuredHeight,
224
+ )
225
+
226
+ val iconWidth = copyButton.drawable?.intrinsicWidth ?: copyButton.measuredWidth
227
+ val iconSlack = ((copyButton.measuredWidth - iconWidth) / 2).coerceAtLeast(0)
228
+ val buttonLeft = (width - inset - copyButton.measuredWidth + iconSlack).coerceAtLeast(0)
229
+ val buttonTop = (headerH - languageView.measuredHeight / 2 - copyButton.measuredHeight / 2).coerceAtLeast(0)
230
+ copyButton.layout(
231
+ buttonLeft,
232
+ buttonTop,
233
+ buttonLeft + copyButton.measuredWidth,
234
+ buttonTop + copyButton.measuredHeight,
235
+ )
236
+
237
+ scrollView.layout(borderW, headerH, width - borderW, bottom - top - borderW)
238
+ }
239
+
240
+ // Divider between the header and the code, centered in the gap the code
241
+ // text's top inset creates, so it adds no height of its own.
242
+ override fun onDraw(canvas: Canvas) {
243
+ super.onDraw(canvas)
244
+ val borderWidth = borderW.toFloat()
245
+ val y = headerH + inset / 2f
246
+ canvas.drawLine(borderWidth, y, width - borderWidth, y, dividerPaint)
247
+ }
248
+
249
+ // Returns whether a menu was shown, so the long-press listener only consumes
250
+ // the event when there is one (a pending block has no menu yet, and a hidden
251
+ // one never does).
252
+ private fun showContextMenu(anchor: View): Boolean {
253
+ if (pending || contextMenuHidden) return false
254
+ ContextMenuPopup.show(anchor, this) {
255
+ item(ContextMenuPopup.Icon.COPY, copyLabel) { copyCode() }
256
+ item(ContextMenuPopup.Icon.DOCUMENT, copyAsMarkdownLabel) { copyFencedMarkdown() }
257
+ }
258
+ return true
259
+ }
260
+
261
+ private fun copyCode() {
262
+ if (pending || code.isEmpty()) return
263
+ copyToClipboard(code)
264
+ onCopyPress?.invoke(code, language ?: "")
265
+ }
266
+
267
+ private fun copyFencedMarkdown() {
268
+ if (code.isEmpty()) return
269
+ copyToClipboard(CodeBlockNode.fencedMarkdown(code, language, fenceChar))
270
+ }
271
+
272
+ private fun copyToClipboard(text: String) {
273
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
274
+ clipboard.setPrimaryClip(ClipData.newPlainText("Code", text))
275
+ }
276
+
277
+ private class CopyIconDrawable(
278
+ color: Int,
279
+ private val size: Int,
280
+ ) : Drawable() {
281
+ private val paint =
282
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
283
+ style = Paint.Style.STROKE
284
+ strokeWidth = size / 20f
285
+ strokeJoin = Paint.Join.ROUND
286
+ strokeCap = Paint.Cap.ROUND
287
+ this.color = color
288
+ }
289
+
290
+ private val glyph = CopyGlyph()
291
+
292
+ override fun draw(canvas: Canvas) {
293
+ glyph.draw(canvas, bounds.width().toFloat(), paint)
294
+ }
295
+
296
+ override fun getIntrinsicWidth() = size
297
+
298
+ override fun getIntrinsicHeight() = size
299
+
300
+ override fun setAlpha(alpha: Int) {
301
+ paint.alpha = alpha
302
+ }
303
+
304
+ override fun setColorFilter(colorFilter: ColorFilter?) {
305
+ paint.colorFilter = colorFilter
306
+ }
307
+
308
+ @Deprecated("Deprecated in Java")
309
+ override fun getOpacity() = PixelFormat.TRANSLUCENT
310
+ }
311
+
312
+ companion object {
313
+ private const val HEADER_LABEL_SCALE = 0.85f
314
+
315
+ // Drawable box for the copy glyph, as a multiple of the code font size.
316
+ // iOS sizes the SF Symbol by point size (kENRMHeaderIconScale = 0.72) but
317
+ // the symbol renders roughly half again as tall as its point size; this box
318
+ // is scaled up to land the drawn glyph at the same on-screen size.
319
+ private const val HEADER_ICON_SCALE = 1.08f
320
+
321
+ private val headerTypeface: Typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL)
322
+
323
+ private fun secondaryColor(color: Int): Int = (color and 0x00FFFFFF) or (0x99 shl 24)
324
+
325
+ private fun dividerColor(color: Int): Int = (color and 0x00FFFFFF) or (0x33 shl 24)
326
+
327
+ private fun contentInset(style: CodeBlockStyle): Int = ceil(style.padding + style.borderWidth).toInt()
328
+
329
+ private fun borderPx(style: CodeBlockStyle): Int = ceil(style.borderWidth).toInt()
330
+
331
+ // Header is the top content inset plus one label line; the code text
332
+ // view's own top inset then forms the single gap below the label.
333
+ private fun headerHeight(style: CodeBlockStyle): Int {
334
+ val paint =
335
+ TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
336
+ textSize = style.fontSize * HEADER_LABEL_SCALE
337
+ typeface = headerTypeface
338
+ }
339
+ val metrics = paint.fontMetrics
340
+ return contentInset(style) + ceil(metrics.descent - metrics.ascent).toInt()
341
+ }
342
+
343
+ private fun createCodePaint(
344
+ style: CodeBlockStyle,
345
+ context: Context,
346
+ ): TextPaint =
347
+ TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
348
+ applyCodeBlockTextStyle(style, context)
349
+ }
350
+
351
+ private fun buildCodeText(
352
+ code: String,
353
+ style: CodeBlockStyle,
354
+ ): SpannableString =
355
+ SpannableString(code).apply {
356
+ if (style.lineHeight > 0f && isNotEmpty()) {
357
+ setSpan(LineHeightSpan(style.lineHeight), 0, length, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
358
+ }
359
+ }
360
+
361
+ // Lines never wrap (the code pane scrolls horizontally), so the layout is
362
+ // built at the text's desired width and the result is independent of the
363
+ // available width.
364
+ fun measureCodeBlockNodeHeight(
365
+ node: MarkdownASTNode,
366
+ config: StyleConfig,
367
+ context: Context,
368
+ @Suppress("unused") width: Float,
369
+ ): Float {
370
+ val style = config.codeBlockStyle
371
+ val inset = contentInset(style)
372
+ val headerH = headerHeight(style)
373
+ val text = buildCodeText(CodeBlockNode.extractCode(node), style)
374
+ if (text.isEmpty()) return headerH + inset * 2f
375
+ val paint = createCodePaint(style, context)
376
+ val contentWidth = ceil(Layout.getDesiredWidth(text, paint)).toInt().coerceAtLeast(1)
377
+ val layout =
378
+ StaticLayout.Builder
379
+ .obtain(text, 0, text.length, paint, contentWidth)
380
+ .setIncludePad(false)
381
+ .setLineSpacing(0f, 1f)
382
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
383
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
384
+ .build()
385
+ return layout.height.toFloat() + inset * 2 + headerH
386
+ }
387
+ }
388
+ }
@@ -183,16 +183,19 @@ object ContextMenuPopup {
183
183
  color = Color.parseColor("#333333")
184
184
  }
185
185
 
186
+ private val copyGlyph = CopyGlyph()
187
+
188
+ // COPY mirrors SF Symbol doc.on.doc, DOCUMENT mirrors doc.text; the copy
189
+ // glyph is shared with the header button in CodeBlockContainerView.
186
190
  override fun draw(canvas: Canvas) {
187
191
  val u = bounds.width() / 24f
188
192
  if (icon == Icon.COPY) {
189
- canvas.drawRoundRect(RectF(8 * u, 2 * u, 22 * u, 16 * u), 2 * u, 2 * u, paint)
190
- canvas.drawRoundRect(RectF(2 * u, 8 * u, 16 * u, 22 * u), 2 * u, 2 * u, paint)
193
+ copyGlyph.draw(canvas, bounds.width().toFloat(), paint)
191
194
  } else {
192
- canvas.drawRoundRect(RectF(4 * u, 2 * u, 20 * u, 22 * u), 2 * u, 2 * u, paint)
193
- canvas.drawLine(8 * u, 8 * u, 16 * u, 8 * u, paint)
194
- canvas.drawLine(8 * u, 12 * u, 16 * u, 12 * u, paint)
195
- canvas.drawLine(8 * u, 16 * u, 13 * u, 16 * u, paint)
195
+ canvas.drawRoundRect(RectF(5 * u, 3 * u, 19 * u, 21 * u), 2.5f * u, 2.5f * u, paint)
196
+ canvas.drawLine(8.5f * u, 9 * u, 15.5f * u, 9 * u, paint)
197
+ canvas.drawLine(8.5f * u, 12.5f * u, 15.5f * u, 12.5f * u, paint)
198
+ canvas.drawLine(8.5f * u, 16 * u, 13 * u, 16 * u, paint)
196
199
  }
197
200
  }
198
201
 
@@ -0,0 +1,101 @@
1
+ package com.swmansion.enriched.markdown.views
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.Paint
5
+ import android.graphics.Path
6
+ import android.graphics.PorterDuff
7
+ import android.graphics.PorterDuffXfermode
8
+
9
+ /**
10
+ * Draws the copy glyph so Android matches the SF Symbol doc.on.doc rendered on
11
+ * iOS: two portrait document pages with a folded (dog-eared) top-right corner,
12
+ * a front page in the lower-left laid over a back page in the upper-right.
13
+ *
14
+ * The back page is punched out where the front page (plus a hairline of
15
+ * clearance) covers it, so the front reads as sitting on top exactly as the SF
16
+ * Symbol does, instead of both outlines crossing. The clear is done inside an
17
+ * offscreen layer so the punched region reveals whatever background the drawable
18
+ * sits on, without the glyph needing to know that color.
19
+ *
20
+ * One instance is held per drawable rather than shared: the paths and clear
21
+ * paint are reused across draws to keep draw() allocation-free, and two drawables
22
+ * of different sizes must not share those buffers. All coordinates use a 24-unit
23
+ * grid scaled to the drawable width, matching the grid the iOS symbol is authored
24
+ * on.
25
+ */
26
+ internal class CopyGlyph {
27
+ private val back = Path()
28
+ private val front = Path()
29
+ private val backFold = Path()
30
+ private val frontFold = Path()
31
+
32
+ // Color and colorFilter are irrelevant under PorterDuff.CLEAR; only the
33
+ // widened stroke (set per draw) matters, so this is configured once.
34
+ private val clearPaint =
35
+ Paint(Paint.ANTI_ALIAS_FLAG).apply {
36
+ style = Paint.Style.FILL_AND_STROKE
37
+ strokeJoin = Paint.Join.ROUND
38
+ strokeCap = Paint.Cap.ROUND
39
+ xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
40
+ }
41
+
42
+ fun draw(
43
+ canvas: Canvas,
44
+ width: Float,
45
+ stroke: Paint,
46
+ ) {
47
+ val u = width / 24f
48
+ val fold = 4f * u
49
+ val corner = 2f * u
50
+
51
+ buildPage(back, 9 * u, 1 * u, 20 * u, 17 * u, fold, corner)
52
+ buildPage(front, 3 * u, 7 * u, 14 * u, 23 * u, fold, corner)
53
+ buildFold(backFold, 20 * u, 1 * u, fold)
54
+ buildFold(frontFold, 14 * u, 7 * u, fold)
55
+ clearPaint.strokeWidth = stroke.strokeWidth * 3f
56
+
57
+ val saved = canvas.saveLayer(0f, 0f, width, width, null)
58
+ canvas.drawPath(back, stroke)
59
+ canvas.drawPath(backFold, stroke)
60
+ canvas.drawPath(front, clearPaint)
61
+ canvas.drawPath(front, stroke)
62
+ canvas.drawPath(frontFold, stroke)
63
+ canvas.restoreToCount(saved)
64
+ }
65
+
66
+ // A page outline whose top-right corner is cut back by fold, with the other
67
+ // three corners rounded by corner.
68
+ private fun buildPage(
69
+ path: Path,
70
+ l: Float,
71
+ t: Float,
72
+ r: Float,
73
+ b: Float,
74
+ fold: Float,
75
+ corner: Float,
76
+ ) {
77
+ path.rewind()
78
+ path.moveTo(l, t + corner)
79
+ path.quadTo(l, t, l + corner, t)
80
+ path.lineTo(r - fold, t)
81
+ path.lineTo(r, t + fold)
82
+ path.lineTo(r, b - corner)
83
+ path.quadTo(r, b, r - corner, b)
84
+ path.lineTo(l + corner, b)
85
+ path.quadTo(l, b, l, b - corner)
86
+ path.close()
87
+ }
88
+
89
+ // The two inner edges of the folded-over corner, at the page's top-right.
90
+ private fun buildFold(
91
+ path: Path,
92
+ r: Float,
93
+ t: Float,
94
+ fold: Float,
95
+ ) {
96
+ path.rewind()
97
+ path.moveTo(r - fold, t)
98
+ path.lineTo(r - fold, t + fold)
99
+ path.lineTo(r, t + fold)
100
+ }
101
+ }
@@ -43,7 +43,7 @@ class TableContainerView(
43
43
  private val styleConfig: StyleConfig,
44
44
  ) : FrameLayout(context),
45
45
  BlockSegmentView {
46
- private val tableStyle: TableStyle = styleConfig.tableStyle
46
+ internal val tableStyle: TableStyle = styleConfig.tableStyle
47
47
 
48
48
  override val segmentMarginTop: Int get() = tableStyle.marginTop.toInt()
49
49
  override val segmentMarginBottom: Int get() = tableStyle.marginBottom.toInt()
@@ -63,6 +63,7 @@ class TableContainerView(
63
63
 
64
64
  var copyLabel: String = ""
65
65
  var copyAsMarkdownLabel: String = ""
66
+ var contextMenuHidden: Boolean = false
66
67
 
67
68
  private val scrollView =
68
69
  HorizontalScrollView(context).apply {
@@ -327,16 +328,42 @@ class TableContainerView(
327
328
  right: Int,
328
329
  bottom: Int,
329
330
  ) {
330
- scrollView.layout(0, 0, right - left, bottom - top)
331
331
  val viewWidth = right - left
332
- scrollView.isHorizontalScrollBarEnabled = totalTableWidth > viewWidth
332
+ val overhang = max(ceil(tableStyle.horizontalOverflow.toDouble()).toInt(), 0)
333
+ val contentWidth = viewWidth - overhang * 2
334
+ val tableOverflows = totalTableWidth > contentWidth
335
+
336
+ scrollView.setPadding(overhang, 0, overhang, 0)
337
+ scrollView.clipToPadding = !tableOverflows
338
+ scrollView.isHorizontalScrollBarEnabled = tableOverflows
339
+
340
+ scrollView.layout(0, 0, viewWidth, bottom - top)
341
+
342
+ gridContainer.translationX =
343
+ if (tableStyle.align.isNotEmpty() && !tableOverflows) {
344
+ val freeSpace = max(contentWidth - totalTableWidth, 0f)
345
+ val desiredLeft =
346
+ overhang +
347
+ when (tableStyle.align) {
348
+ "center" -> freeSpace / 2f
349
+ "right" -> freeSpace
350
+ else -> 0f
351
+ }
352
+ desiredLeft - gridContainer.left
353
+ } else {
354
+ 0f
355
+ }
333
356
 
334
- if (isRtl && totalTableWidth > viewWidth) {
335
- scrollView.scrollTo((totalTableWidth - viewWidth).toInt(), 0)
357
+ if (isRtl) {
358
+ val effectiveWidth = if (tableOverflows) contentWidth.toFloat() else viewWidth.toFloat()
359
+ if (totalTableWidth > effectiveWidth) {
360
+ scrollView.scrollTo((totalTableWidth - effectiveWidth).toInt(), 0)
361
+ }
336
362
  }
337
363
  }
338
364
 
339
365
  private fun showContextMenu(anchor: View) {
366
+ if (contextMenuHidden) return
340
367
  val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
341
368
  ContextMenuPopup.show(anchor, this) {
342
369
  item(ContextMenuPopup.Icon.COPY, copyLabel) {
@@ -25,8 +25,17 @@ endif()
25
25
 
26
26
  set(ANDROID_CPP_DIR "${LIB_ANDROID_DIR}/src/main/cpp")
27
27
 
28
+ # Optional tree-sitter code highlighting. build.gradle writes this config during
29
+ # configuration (enable flag, selected languages, generated registry dir); absent
30
+ # on a bare CMake invocation, in which case highlighting stays compiled out.
31
+ set(HIGHLIGHT_CONFIG "${LIB_ANDROID_DIR}/build/generated/highlight/highlight-config.cmake")
32
+ if(EXISTS "${HIGHLIGHT_CONFIG}")
33
+ include("${HIGHLIGHT_CONFIG}")
34
+ endif()
35
+
28
36
  file(GLOB MD4C_SOURCES "${CPP_ROOT}/md4c/*.c")
29
37
  file(GLOB PARSER_SOURCES "${CPP_ROOT}/parser/*.cpp")
38
+ file(GLOB HIGHLIGHT_SOURCES "${CPP_ROOT}/highlight/*.cpp")
30
39
 
31
40
  # md4c is C code, build as separate static library with C flags
32
41
  add_library(md4c_lib STATIC ${MD4C_SOURCES})
@@ -46,6 +55,7 @@ add_library(
46
55
  ${LIB_CODEGEN_SRCS}
47
56
  ${LIB_CUSTOM_JNI_SRCS}
48
57
  ${PARSER_SOURCES}
58
+ ${HIGHLIGHT_SOURCES}
49
59
  ${ANDROID_CPP_DIR}/jni-adapter.cpp
50
60
  )
51
61
 
@@ -58,6 +68,7 @@ target_include_directories(
58
68
  ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
59
69
  ${CPP_ROOT}/md4c
60
70
  ${CPP_ROOT}/parser
71
+ ${CPP_ROOT}/highlight
61
72
  )
62
73
 
63
74
  target_compile_definitions(${LIB_TARGET_NAME} PRIVATE MD4C_USE_UTF8=1)
@@ -82,3 +93,45 @@ target_include_directories(
82
93
  )
83
94
 
84
95
  target_compile_reactnative_options(${LIB_TARGET_NAME} PRIVATE)
96
+
97
+ # --- Code-block syntax highlighting (tree-sitter) ---
98
+ # CodeBlockHighlighter.cpp / CodeBlockLanguages.cpp are already in the target via
99
+ # HIGHLIGHT_SOURCES; without the define below CodeBlockHighlighter.cpp is the stub.
100
+ # When enabled we add the runtime, the generated registry, and one OBJECT lib per
101
+ # selected grammar (each isolated to its own dir so grammar parser.h files never
102
+ # collide), then define ENRICHED_MARKDOWN_CODE_HIGHLIGHT so the real seam compiles.
103
+ if(ENRICHED_MARKDOWN_CODE_HIGHLIGHT)
104
+ set(TS_VENDOR "${CPP_ROOT}/highlight/vendor")
105
+
106
+ add_library(tree_sitter_runtime STATIC "${TS_VENDOR}/tree-sitter/src/lib.c")
107
+ target_include_directories(tree_sitter_runtime
108
+ PRIVATE "${TS_VENDOR}/tree-sitter/src"
109
+ PUBLIC "${TS_VENDOR}/tree-sitter/include")
110
+ set_target_properties(tree_sitter_runtime PROPERTIES
111
+ C_STANDARD 11 C_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON)
112
+ target_compile_options(tree_sitter_runtime PRIVATE -fvisibility=hidden)
113
+
114
+ set(GRAMMAR_OBJECTS "")
115
+ foreach(lang IN LISTS ENRICHED_MARKDOWN_HIGHLIGHT_LANGUAGES)
116
+ set(gdir "${TS_VENDOR}/grammars/${lang}")
117
+ set(gsrcs "${gdir}/parser.c")
118
+ if(EXISTS "${gdir}/scanner.c")
119
+ list(APPEND gsrcs "${gdir}/scanner.c")
120
+ endif()
121
+ add_library(ts_grammar_${lang} OBJECT ${gsrcs})
122
+ target_include_directories(ts_grammar_${lang} PRIVATE "${gdir}")
123
+ set_target_properties(ts_grammar_${lang} PROPERTIES
124
+ C_STANDARD 11 C_STANDARD_REQUIRED ON POSITION_INDEPENDENT_CODE ON)
125
+ target_compile_options(ts_grammar_${lang} PRIVATE -fvisibility=hidden)
126
+ list(APPEND GRAMMAR_OBJECTS $<TARGET_OBJECTS:ts_grammar_${lang}>)
127
+ endforeach()
128
+
129
+ target_sources(${LIB_TARGET_NAME} PRIVATE
130
+ "${ENRICHED_MARKDOWN_HIGHLIGHT_GENERATED_DIR}/generated_registry.cpp"
131
+ ${GRAMMAR_OBJECTS})
132
+ target_include_directories(${LIB_TARGET_NAME} PRIVATE
133
+ "${TS_VENDOR}/tree-sitter/include"
134
+ "${ENRICHED_MARKDOWN_HIGHLIGHT_GENERATED_DIR}")
135
+ target_compile_definitions(${LIB_TARGET_NAME} PRIVATE ENRICHED_MARKDOWN_CODE_HIGHLIGHT=1)
136
+ target_link_libraries(${LIB_TARGET_NAME} tree_sitter_runtime)
137
+ endif()
@@ -12,6 +12,16 @@ void MarkdownContainerShadowNode::setMeasurementsManager(
12
12
  measurementsManager_ = measurementsManager;
13
13
  }
14
14
 
15
+ void MarkdownContainerShadowNode::dirtyLayoutIfNeeded() {
16
+ const auto state = this->getStateData();
17
+ const auto counter = state.getForceHeightRecalculationCounter();
18
+
19
+ if (forceHeightRecalculationCounter_ != counter) {
20
+ forceHeightRecalculationCounter_ = counter;
21
+ dirtyLayout();
22
+ }
23
+ }
24
+
15
25
  Size MarkdownContainerShadowNode::measureContent(const LayoutContext &layoutContext,
16
26
  const LayoutConstraints &layoutConstraints) const {
17
27
  return measurementsManager_->measure(getSurfaceId(), getTag(), getConcreteProps(), layoutConstraints);