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

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 (505) hide show
  1. package/README.md +54 -8
  2. package/ReactNativeEnrichedMarkdown.podspec +82 -73
  3. package/android/build.gradle +188 -1
  4. package/android/consumer-rules.pro +1 -1
  5. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +27 -0
  6. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +7 -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 +27 -0
  10. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +7 -0
  11. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +223 -0
  12. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +183 -0
  13. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +65 -0
  14. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +1086 -61
  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 +351 -13
  18. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +62 -0
  19. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +85 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +160 -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 +76 -0
  23. package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +126 -10
  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/events/CopyRequestEvent.kt +28 -0
  26. package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLayoutEvent.kt +36 -0
  27. package/android/src/main/java/com/swmansion/enriched/markdown/events/SelectionChangeEvent.kt +43 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +54 -4
  29. package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +923 -462
  30. package/android/src/main/java/com/swmansion/enriched/markdown/input/MarkdownClipboard.kt +42 -0
  31. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/BlockEditCoordinator.kt +196 -0
  32. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/ClipboardCoordinator.kt +73 -0
  33. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditContext.kt +19 -0
  34. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditPipeline.kt +248 -0
  35. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/EditSession.kt +52 -0
  36. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +108 -7
  37. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/LinkCoordinator.kt +69 -0
  38. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MarkdownTextWatcher.kt +3 -3
  39. package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/MentionCoordinator.kt +118 -0
  40. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnChangeStateEvent.kt +26 -0
  41. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnContextMenuItemPressEvent.kt +26 -0
  42. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnInputKeyPressEvent.kt +24 -0
  43. package/android/src/main/java/com/swmansion/enriched/markdown/input/events/OnPasteEvent.kt +24 -0
  44. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockStore.kt +254 -0
  45. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +80 -87
  46. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputFormatter.kt +109 -1
  47. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +116 -22
  48. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/MarkdownSerializer.kt +167 -38
  49. package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/RangeEditAdjustment.kt +149 -0
  50. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputEventEmitter.kt +66 -5
  51. package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +4 -2
  52. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockRange.kt +27 -0
  53. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/BlockType.kt +48 -0
  54. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/FormattingRange.kt +9 -4
  55. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/InputFormatterStyle.kt +39 -2
  56. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathAtom.kt +20 -0
  57. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +1 -1
  58. package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MutableRangeBounds.kt +11 -0
  59. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputBulletSpan.kt +82 -0
  60. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputHeadingSpan.kt +58 -0
  61. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputListItemSpacingSpan.kt +37 -0
  62. package/android/src/main/java/com/swmansion/enriched/markdown/input/spans/InputOrderedListMarkerSpan.kt +63 -0
  63. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/BlockHandler.kt +54 -0
  64. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/HeadingBlockHandler.kt +27 -0
  65. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/OrderedListBlockHandler.kt +38 -0
  66. package/android/src/main/java/com/swmansion/enriched/markdown/input/styles/UnorderedListBlockHandler.kt +37 -0
  67. package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +8 -26
  68. package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +1 -0
  69. package/android/src/main/java/com/swmansion/enriched/markdown/parser/Parser.kt +1 -0
  70. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockStyleContext.kt +9 -2
  71. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/BlockquoteRenderer.kt +100 -12
  72. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/CodeBlockRenderer.kt +16 -1
  73. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +4 -0
  74. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +37 -7
  75. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListContextManager.kt +2 -1
  76. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListItemRenderer.kt +74 -18
  77. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/ListRenderer.kt +43 -4
  78. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/NodeRenderer.kt +9 -1
  79. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SoftBreakRenderer.kt +16 -0
  80. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +35 -28
  81. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BaseListSpan.kt +24 -0
  82. package/android/src/main/java/com/swmansion/enriched/markdown/spans/BlockquoteSpan.kt +174 -24
  83. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +20 -82
  84. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBlockSpan.kt +13 -16
  85. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeSpan.kt +1 -1
  86. package/android/src/main/java/com/swmansion/enriched/markdown/spans/EmphasisSpan.kt +1 -1
  87. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ImageSpan.kt +182 -46
  88. package/android/src/main/java/com/swmansion/enriched/markdown/spans/InlineChipPainter.kt +140 -0
  89. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkChipSpans.kt +177 -0
  90. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +1 -6
  91. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListItemSpacingSpan.kt +33 -0
  92. package/android/src/main/java/com/swmansion/enriched/markdown/spans/ListMarkerAnchorSpan.kt +38 -0
  93. package/android/src/main/java/com/swmansion/enriched/markdown/spans/OrderedListSpan.kt +2 -0
  94. package/android/src/main/java/com/swmansion/enriched/markdown/spans/StrongSpan.kt +1 -1
  95. package/android/src/main/java/com/swmansion/enriched/markdown/spans/TaskListSpan.kt +3 -2
  96. package/android/src/main/java/com/swmansion/enriched/markdown/spans/UnorderedListSpan.kt +3 -2
  97. package/android/src/main/java/com/swmansion/enriched/markdown/styles/BlockquoteStyle.kt +6 -0
  98. package/android/src/main/java/com/swmansion/enriched/markdown/styles/CodeBlockStyle.kt +26 -0
  99. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ImageStyle.kt +7 -1
  100. package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +16 -1
  101. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +36 -1
  102. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkVariantEntry.kt +7 -3
  103. package/android/src/main/java/com/swmansion/enriched/markdown/styles/ListStyle.kt +4 -1
  104. package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +3 -0
  105. package/android/src/main/java/com/swmansion/enriched/markdown/styles/StyleConfig.kt +13 -4
  106. package/android/src/main/java/com/swmansion/enriched/markdown/styles/TableStyle.kt +6 -0
  107. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockHighlighter.kt +90 -0
  108. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/CodeBlockNode.kt +57 -0
  109. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/FeatureFlags.kt +1 -0
  110. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownSegment.kt +9 -0
  111. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +84 -7
  112. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/ReadableMapExtensions.kt +3 -0
  113. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/RenderedSegment.kt +10 -0
  114. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/SegmentSignature.kt +1 -0
  115. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/StreamingMarkdownFilter.kt +83 -5
  116. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/serialization/MarkdownASTSerializer.kt +4 -0
  117. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +74 -44
  118. package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +52 -0
  119. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageCache.kt +26 -3
  120. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/ImageDownloader.kt +22 -9
  121. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/LocalImageLoader.kt +161 -0
  122. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/HTMLGenerator.kt +2 -1
  123. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/MarkdownExtractor.kt +6 -0
  124. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/TextPaintExtensions.kt +23 -0
  125. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/CheckboxTouchHelper.kt +7 -0
  126. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/interaction/TaskListTapUtils.kt +41 -21
  127. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/span/BlockSpacing.kt +15 -8
  128. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/HeadlessActionMode.kt +88 -0
  129. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt +100 -0
  130. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt +58 -27
  131. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +72 -16
  132. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionChangeReporter.kt +232 -0
  133. package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +64 -0
  134. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +414 -0
  135. package/android/src/main/java/com/swmansion/enriched/markdown/views/ContextMenuPopup.kt +9 -6
  136. package/android/src/main/java/com/swmansion/enriched/markdown/views/CopyGlyph.kt +101 -0
  137. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +61 -11
  138. package/android/src/main/jni/CMakeLists.txt +53 -0
  139. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.cpp +10 -0
  140. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownContainerShadowNode.h +11 -2
  141. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.cpp +10 -0
  142. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextShadowNode.h +12 -2
  143. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.cpp +9 -0
  144. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/MarkdownTextState.h +24 -0
  145. package/android/src/main/jni/react/renderer/components/EnrichedMarkdownTextSpec/conversions.h +16 -0
  146. package/android/src/math/java/com/swmansion/enriched/markdown/input/math/MathChipControllerImpl.kt +21 -4
  147. package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +8 -2
  148. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
  149. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +13 -6
  150. package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathMeasureHelper.kt +2 -1
  151. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +26 -8
  152. package/cpp/highlight/CodeBlockHighlighter.cpp +284 -0
  153. package/cpp/highlight/CodeBlockHighlighter.hpp +66 -0
  154. package/cpp/highlight/CodeBlockLanguages.cpp +138 -0
  155. package/cpp/highlight/CodeBlockLanguages.hpp +25 -0
  156. package/cpp/highlight/HighlightGrammars.hpp +28 -0
  157. package/cpp/highlight/code_highlight_podspec.rb +211 -0
  158. package/cpp/highlight/gen-registry.mjs +155 -0
  159. package/cpp/highlight/grammar-versions.json +159 -0
  160. package/cpp/highlight/vendor/generated/generated_queries.h +1665 -0
  161. package/cpp/highlight/vendor/generated/generated_registry.cpp +59 -0
  162. package/cpp/parser/MD4CParser.cpp +173 -61
  163. package/cpp/parser/MD4CParser.hpp +1 -0
  164. package/cpp/parser/MarkdownASTNode.hpp +2 -1
  165. package/cpp/wasm/ASTSerializer.cpp +2 -0
  166. package/cpp/wasm/build.sh +1 -0
  167. package/cpp/wasm/md4c_wasm.cpp +3 -1
  168. package/docs/ACCESSIBILITY.md +14 -2
  169. package/docs/API_REFERENCE.md +405 -12
  170. package/docs/BREAKING_CHANGES.md +129 -0
  171. package/docs/CODE_HIGHLIGHT.md +133 -0
  172. package/docs/COPY_OPTIONS.md +61 -3
  173. package/docs/ELEMENTS_STRUCTURE.md +52 -4
  174. package/docs/IMAGE_CACHING.md +30 -0
  175. package/docs/INPUT.md +110 -11
  176. package/docs/LATEX_MATH.md +87 -45
  177. package/docs/MACOS.md +1 -1
  178. package/docs/MARKDOWN_STREAMING.md +27 -1
  179. package/docs/MENTIONS.md +8 -1
  180. package/docs/NATIVE_ASSETS.md +127 -0
  181. package/docs/RTL.md +1 -1
  182. package/docs/STYLES.md +73 -6
  183. package/docs/TESTING.md +112 -0
  184. package/docs/TEXT.md +18 -6
  185. package/docs/WEB.md +4 -1
  186. package/ios/EnrichedMarkdown.h +7 -0
  187. package/ios/EnrichedMarkdown.mm +583 -76
  188. package/ios/EnrichedMarkdownText.h +7 -0
  189. package/ios/EnrichedMarkdownText.mm +299 -73
  190. package/ios/attachments/ENRMImageAttachment.h +9 -0
  191. package/ios/attachments/ENRMImageAttachment.m +143 -40
  192. package/ios/attachments/ENRMMathInlineAttachment+macOS.m +2 -1
  193. package/ios/attachments/ENRMMathInlineAttachment.h +5 -0
  194. package/ios/attachments/ENRMMathInlineAttachment.m +50 -82
  195. package/ios/attachments/ENRMMathInlineAttachmentShared.h +2 -0
  196. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +223 -0
  197. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +183 -0
  198. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +65 -0
  199. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +1086 -61
  200. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +181 -2
  201. package/ios/input/ENRMAutoLinkDetector.h +2 -1
  202. package/ios/input/ENRMAutoLinkDetector.mm +79 -41
  203. package/ios/input/ENRMBlockEditCoordinator.h +71 -0
  204. package/ios/input/ENRMBlockEditCoordinator.mm +195 -0
  205. package/ios/input/ENRMBlockRange.h +28 -0
  206. package/ios/input/ENRMBlockRange.mm +30 -0
  207. package/ios/input/ENRMBlockStore.h +56 -0
  208. package/ios/input/ENRMBlockStore.mm +275 -0
  209. package/ios/input/ENRMClipboardCoordinator.h +45 -0
  210. package/ios/input/ENRMClipboardCoordinator.mm +120 -0
  211. package/ios/input/ENRMDetectorPipeline.h +2 -1
  212. package/ios/input/ENRMEditPipeline.h +76 -0
  213. package/ios/input/ENRMEditPipeline.mm +228 -0
  214. package/ios/input/ENRMEditSession.h +36 -0
  215. package/ios/input/ENRMEditSession.mm +63 -0
  216. package/ios/input/ENRMFormattingStore.h +17 -0
  217. package/ios/input/ENRMFormattingStore.mm +95 -105
  218. package/ios/input/ENRMInputBlockType.h +91 -0
  219. package/ios/input/ENRMInputBlockType.mm +5 -0
  220. package/ios/input/ENRMInputDecorationDrawer.h +18 -0
  221. package/ios/input/ENRMInputEventEmitter.h +56 -0
  222. package/ios/input/ENRMInputEventEmitter.mm +308 -0
  223. package/ios/input/ENRMInputFormatter.h +29 -69
  224. package/ios/input/ENRMInputFormatter.mm +216 -124
  225. package/ios/input/ENRMInputFormatterStyle.h +99 -0
  226. package/ios/input/ENRMInputFormatterStyle.mm +188 -0
  227. package/ios/input/ENRMInputLayoutManager.h +9 -0
  228. package/ios/input/ENRMInputLayoutManager.mm +30 -0
  229. package/ios/input/ENRMInputListMarkerDrawer.h +32 -0
  230. package/ios/input/ENRMInputListMarkerDrawer.mm +426 -0
  231. package/ios/input/ENRMInputParser.h +5 -6
  232. package/ios/input/ENRMInputParser.mm +461 -178
  233. package/ios/input/ENRMInputRemend.mm +2 -2
  234. package/ios/input/ENRMInputStyleStateBuilder.h +40 -0
  235. package/ios/input/ENRMInputStyleStateBuilder.mm +60 -0
  236. package/ios/input/ENRMInputTextView.mm +103 -8
  237. package/ios/input/ENRMInputTypingAttributesController.h +42 -0
  238. package/ios/input/ENRMInputTypingAttributesController.mm +203 -0
  239. package/ios/input/ENRMLinkCoordinator.h +41 -0
  240. package/ios/input/ENRMLinkCoordinator.mm +89 -0
  241. package/ios/input/ENRMMarkdownSerializer.h +29 -5
  242. package/ios/input/ENRMMarkdownSerializer.mm +170 -48
  243. package/ios/input/ENRMMathAtom.h +26 -0
  244. package/ios/input/ENRMMathAtom.mm +12 -0
  245. package/ios/input/ENRMMentionCoordinator.h +45 -0
  246. package/ios/input/ENRMMentionCoordinator.mm +167 -0
  247. package/ios/input/ENRMRangeEditAdjustment.h +38 -0
  248. package/ios/input/ENRMRangeEditAdjustment.mm +91 -0
  249. package/ios/input/ENRMRangeStoreUtils.h +39 -0
  250. package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +6 -6
  251. package/ios/input/EnrichedMarkdownTextInput+Internal.h +11 -3
  252. package/ios/input/EnrichedMarkdownTextInput.h +7 -1
  253. package/ios/input/EnrichedMarkdownTextInput.mm +1089 -1023
  254. package/ios/input/InputStylePropsUtils.h +109 -68
  255. package/ios/input/internals/EnrichedMarkdownTextInputShadowNode.mm +2 -1
  256. package/ios/input/styles/ENRMBlockHandler.h +46 -0
  257. package/ios/input/styles/ENRMHeadingBlockHandler.h +15 -0
  258. package/ios/input/styles/ENRMHeadingBlockHandler.mm +38 -0
  259. package/ios/input/styles/ENRMOrderedListBlockHandler.h +14 -0
  260. package/ios/input/styles/ENRMOrderedListBlockHandler.mm +48 -0
  261. package/ios/input/styles/ENRMUnorderedListBlockHandler.h +15 -0
  262. package/ios/input/styles/ENRMUnorderedListBlockHandler.mm +51 -0
  263. package/ios/internals/ENRMViewFreeMeasurement.h +331 -0
  264. package/ios/internals/EnrichedMarkdownShadowNode.h +1 -3
  265. package/ios/internals/EnrichedMarkdownShadowNode.mm +16 -20
  266. package/ios/internals/EnrichedMarkdownTextShadowNode.h +1 -3
  267. package/ios/internals/EnrichedMarkdownTextShadowNode.mm +15 -20
  268. package/ios/internals/MeasurementCache.h +41 -9
  269. package/ios/internals/ShadowMeasurementUtils.h +63 -58
  270. package/ios/math/ENRMMathChipController.h +74 -0
  271. package/ios/math/ENRMMathChipController.mm +476 -0
  272. package/ios/math/ENRMRaTeXBridge.swift +27 -6
  273. package/ios/parser/ENRMMarkdownParser.h +1 -8
  274. package/ios/parser/ENRMMarkdownParser.mm +2 -11
  275. package/ios/parser/MarkdownASTNode.h +2 -1
  276. package/ios/parser/MarkdownParserBridge.mm +4 -0
  277. package/ios/renderer/AttributedRenderer.m +20 -2
  278. package/ios/renderer/BaseRenderer.h +17 -2
  279. package/ios/renderer/BaseRenderer.m +30 -0
  280. package/ios/renderer/BlockquoteRenderer.h +1 -1
  281. package/ios/renderer/BlockquoteRenderer.m +144 -35
  282. package/ios/renderer/CodeBlockRenderer.h +1 -1
  283. package/ios/renderer/CodeBlockRenderer.m +27 -23
  284. package/ios/renderer/CodeRenderer.h +1 -1
  285. package/ios/renderer/CodeRenderer.m +3 -1
  286. package/ios/renderer/ENRMImageRenderer.h +1 -1
  287. package/ios/renderer/ENRMImageRenderer.m +5 -1
  288. package/ios/renderer/ENRMMathInlineRenderer.h +1 -1
  289. package/ios/renderer/ENRMMathInlineRenderer.m +6 -1
  290. package/ios/renderer/ENRMSpoilerRenderer.h +1 -1
  291. package/ios/renderer/ENRMSpoilerRenderer.m +3 -1
  292. package/ios/renderer/EmphasisRenderer.h +1 -1
  293. package/ios/renderer/EmphasisRenderer.m +3 -1
  294. package/ios/renderer/HeadingRenderer.h +1 -1
  295. package/ios/renderer/HeadingRenderer.m +13 -6
  296. package/ios/renderer/HighlightRenderer.h +1 -1
  297. package/ios/renderer/HighlightRenderer.m +3 -1
  298. package/ios/renderer/LinkRenderer.h +1 -1
  299. package/ios/renderer/LinkRenderer.m +50 -16
  300. package/ios/renderer/ListItemRenderer.h +13 -1
  301. package/ios/renderer/ListItemRenderer.m +102 -44
  302. package/ios/renderer/ListRenderer.h +2 -2
  303. package/ios/renderer/ListRenderer.m +63 -25
  304. package/ios/renderer/ParagraphRenderer.h +1 -1
  305. package/ios/renderer/ParagraphRenderer.m +16 -8
  306. package/ios/renderer/RenderContext.h +10 -0
  307. package/ios/renderer/RenderContext.m +44 -0
  308. package/ios/renderer/RendererFactory.m +14 -0
  309. package/ios/renderer/StrikethroughRenderer.h +1 -1
  310. package/ios/renderer/StrikethroughRenderer.m +3 -1
  311. package/ios/renderer/StrongRenderer.h +1 -1
  312. package/ios/renderer/StrongRenderer.m +3 -1
  313. package/ios/renderer/SubscriptRenderer.h +1 -1
  314. package/ios/renderer/SubscriptRenderer.m +3 -1
  315. package/ios/renderer/SuperscriptRenderer.h +1 -1
  316. package/ios/renderer/SuperscriptRenderer.m +3 -1
  317. package/ios/renderer/TextRenderer.h +2 -2
  318. package/ios/renderer/TextRenderer.m +3 -1
  319. package/ios/renderer/ThematicBreakRenderer.h +1 -1
  320. package/ios/renderer/ThematicBreakRenderer.m +3 -1
  321. package/ios/renderer/UnderlineRenderer.h +1 -1
  322. package/ios/renderer/UnderlineRenderer.m +3 -1
  323. package/ios/styles/StyleConfig.h +42 -0
  324. package/ios/styles/StyleConfig.mm +216 -0
  325. package/ios/utils/BlockquoteBorder.h +6 -0
  326. package/ios/utils/BlockquoteBorder.m +249 -52
  327. package/ios/utils/CodeBackground.m +3 -65
  328. package/ios/utils/CodeBlockBackground.m +16 -2
  329. package/ios/utils/ENRMAtomicSize.h +65 -0
  330. package/ios/utils/ENRMBlockMenu.h +66 -0
  331. package/ios/utils/ENRMChipDrawing.h +24 -0
  332. package/ios/utils/ENRMChipDrawing.m +83 -0
  333. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  334. package/ios/utils/ENRMCodeBlockContent.mm +79 -0
  335. package/ios/utils/ENRMCodeBlockHighlighter.h +37 -0
  336. package/ios/utils/ENRMCodeBlockHighlighter.mm +51 -0
  337. package/ios/utils/ENRMFeatureFlags.h +7 -0
  338. package/ios/utils/ENRMImageDownloader.h +11 -1
  339. package/ios/utils/ENRMImageDownloader.m +60 -16
  340. package/ios/utils/ENRMLinkChip.h +70 -0
  341. package/ios/utils/ENRMLinkChip.m +213 -0
  342. package/ios/utils/ENRMLinkLayout.h +50 -0
  343. package/ios/utils/ENRMLinkLayout.m +45 -0
  344. package/ios/utils/ENRMLocalImageLoader.h +31 -0
  345. package/ios/utils/ENRMLocalImageLoader.m +68 -0
  346. package/ios/utils/ENRMMathFallback.h +26 -0
  347. package/ios/utils/ENRMMathFallback.m +18 -0
  348. package/ios/utils/ENRMSelectionChange.h +120 -0
  349. package/ios/utils/ENRMSelectionChange.m +282 -0
  350. package/ios/utils/ENRMTextViewSetup.h +35 -17
  351. package/ios/utils/EditMenuUtils+macOS.m +4 -1
  352. package/ios/utils/EditMenuUtils.h +35 -6
  353. package/ios/utils/EditMenuUtils.m +188 -11
  354. package/ios/utils/HTMLGenerator.m +2 -1
  355. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  356. package/ios/utils/LastElementUtils.h +1 -0
  357. package/ios/utils/ListMarkerDrawer.m +29 -42
  358. package/ios/utils/MarkdownASTSerializer.m +4 -0
  359. package/ios/utils/MarkdownAccessibilityElementBuilder.m +3 -1
  360. package/ios/utils/MarkdownExtractor.m +2 -1
  361. package/ios/utils/ParagraphStyleUtils.h +5 -0
  362. package/ios/utils/ParagraphStyleUtils.m +77 -17
  363. package/ios/utils/RTFExportUtils.m +2 -0
  364. package/ios/utils/RenderedMarkdownSegment.h +13 -1
  365. package/ios/utils/RenderedMarkdownSegment.m +20 -0
  366. package/ios/utils/SegmentRenderer.m +17 -4
  367. package/ios/utils/StreamingMarkdownFilter.h +11 -1
  368. package/ios/utils/StreamingMarkdownFilter.m +98 -2
  369. package/ios/utils/StylePropsUtils.h +110 -1
  370. package/ios/utils/TaskListTapUtils.m +24 -0
  371. package/ios/utils/TextViewLayoutManager.mm +3 -0
  372. package/ios/views/ENRMCodeBlockContainerView.h +60 -0
  373. package/ios/views/ENRMCodeBlockContainerView.m +569 -0
  374. package/ios/views/ENRMMathContainerView.h +23 -0
  375. package/ios/views/ENRMMathContainerView.m +87 -20
  376. package/ios/views/TableContainerView.h +32 -0
  377. package/ios/views/TableContainerView.m +291 -175
  378. package/jest.d.ts +1 -0
  379. package/jest.js +5 -0
  380. package/lib/module/EnrichedMarkdownNativeComponent.ts +203 -1
  381. package/lib/module/EnrichedMarkdownTextInput.js +84 -5
  382. package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
  383. package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  384. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +204 -1
  385. package/lib/module/headingDefaults.js +34 -0
  386. package/lib/module/headingDefaults.js.map +1 -0
  387. package/lib/module/index.js.map +1 -1
  388. package/lib/module/jest/index.js +205 -0
  389. package/lib/module/jest/index.js.map +1 -0
  390. package/lib/module/native/EnrichedMarkdownText.js +199 -15
  391. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  392. package/lib/module/normalizeMarkdownStyle.js +98 -23
  393. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  394. package/lib/module/normalizeMarkdownStyle.web.js +73 -22
  395. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  396. package/lib/module/normalizeMarkdownTextInputStyle.js +62 -15
  397. package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
  398. package/lib/module/normalizeMenuItem.js +1 -1
  399. package/lib/module/utils/textInputState.js +26 -0
  400. package/lib/module/utils/textInputState.js.map +1 -0
  401. package/lib/module/web/EnrichedMarkdownText.js +15 -8
  402. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  403. package/lib/module/web/parseMarkdown.js +4 -3
  404. package/lib/module/web/parseMarkdown.js.map +1 -1
  405. package/lib/module/web/renderers/InlineRenderers.js +102 -3
  406. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  407. package/lib/module/web/renderers/ListRenderers.js +7 -2
  408. package/lib/module/web/renderers/ListRenderers.js.map +1 -1
  409. package/lib/module/web/renderers/index.js +7 -4
  410. package/lib/module/web/renderers/index.js.map +1 -1
  411. package/lib/module/web/styles.js +101 -6
  412. package/lib/module/web/styles.js.map +1 -1
  413. package/lib/module/web/utils.js +14 -0
  414. package/lib/module/web/utils.js.map +1 -1
  415. package/lib/module/web/wasm/md4c.js +0 -0
  416. package/lib/module/web/wasm/md4c.js.map +1 -1
  417. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +185 -2
  418. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  419. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +94 -9
  420. package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
  421. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +69 -5
  422. package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
  423. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +185 -2
  424. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  425. package/lib/typescript/src/headingDefaults.d.ts +9 -0
  426. package/lib/typescript/src/headingDefaults.d.ts.map +1 -0
  427. package/lib/typescript/src/index.d.ts +3 -3
  428. package/lib/typescript/src/index.d.ts.map +1 -1
  429. package/lib/typescript/src/index.web.d.ts +2 -2
  430. package/lib/typescript/src/index.web.d.ts.map +1 -1
  431. package/lib/typescript/src/jest/index.d.ts +6 -0
  432. package/lib/typescript/src/jest/index.d.ts.map +1 -0
  433. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +5 -5
  434. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  435. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  436. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  437. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +19 -5
  438. package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
  439. package/lib/typescript/src/types/MarkdownStyle.d.ts +122 -5
  440. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  441. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +39 -0
  442. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  443. package/lib/typescript/src/types/MarkdownTextProps.d.ts +207 -10
  444. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  445. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +60 -1
  446. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  447. package/lib/typescript/src/types/events.d.ts +85 -0
  448. package/lib/typescript/src/types/events.d.ts.map +1 -1
  449. package/lib/typescript/src/utils/textInputState.d.ts +24 -0
  450. package/lib/typescript/src/utils/textInputState.d.ts.map +1 -0
  451. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  452. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  453. package/lib/typescript/src/web/parseMarkdown.d.ts +1 -1
  454. package/lib/typescript/src/web/parseMarkdown.d.ts.map +1 -1
  455. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  456. package/lib/typescript/src/web/renderers/ListRenderers.d.ts.map +1 -1
  457. package/lib/typescript/src/web/renderers/index.d.ts +2 -1
  458. package/lib/typescript/src/web/renderers/index.d.ts.map +1 -1
  459. package/lib/typescript/src/web/styles.d.ts +2 -1
  460. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  461. package/lib/typescript/src/web/types.d.ts +10 -2
  462. package/lib/typescript/src/web/types.d.ts.map +1 -1
  463. package/lib/typescript/src/web/utils.d.ts +4 -0
  464. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  465. package/package.json +36 -9
  466. package/postinstall.mjs +131 -0
  467. package/ratex-version.json +21 -0
  468. package/src/EnrichedMarkdownNativeComponent.ts +203 -1
  469. package/src/EnrichedMarkdownTextInput.tsx +159 -8
  470. package/src/EnrichedMarkdownTextInputNativeComponent.ts +78 -5
  471. package/src/EnrichedMarkdownTextNativeComponent.ts +204 -1
  472. package/src/headingDefaults.ts +21 -0
  473. package/src/index.tsx +15 -0
  474. package/src/index.web.tsx +6 -1
  475. package/src/jest/index.tsx +236 -0
  476. package/src/native/EnrichedMarkdownText.tsx +260 -11
  477. package/src/normalizeMarkdownStyle.ts +117 -16
  478. package/src/normalizeMarkdownStyle.web.ts +76 -15
  479. package/src/normalizeMarkdownTextInputStyle.ts +110 -35
  480. package/src/normalizeMenuItem.ts +1 -1
  481. package/src/types/MarkdownStyle.ts +125 -5
  482. package/src/types/MarkdownStyleInternal.ts +51 -0
  483. package/src/types/MarkdownTextProps.ts +215 -10
  484. package/src/types/MarkdownTextProps.web.ts +62 -1
  485. package/src/types/events.ts +90 -0
  486. package/src/utils/textInputState.ts +35 -0
  487. package/src/web/EnrichedMarkdownText.tsx +26 -5
  488. package/src/web/parseMarkdown.ts +6 -2
  489. package/src/web/renderers/InlineRenderers.tsx +116 -3
  490. package/src/web/renderers/ListRenderers.tsx +14 -2
  491. package/src/web/renderers/index.tsx +6 -2
  492. package/src/web/styles.ts +114 -5
  493. package/src/web/types.ts +10 -1
  494. package/src/web/utils.ts +9 -0
  495. package/src/web/wasm/md4c.js +0 -0
  496. package/vendor-grammars.mjs +511 -0
  497. package/vendor-ratex.mjs +173 -0
  498. package/app.plugin.js +0 -2
  499. package/plugin/build/withAndroidMath.js +0 -19
  500. package/plugin/build/withIosMath.js +0 -28
  501. package/plugin/build/withReactNativeEnrichedMarkdown.js +0 -11
  502. package/plugin/src/withAndroidMath.ts +0 -24
  503. package/plugin/src/withIosMath.ts +0 -35
  504. package/plugin/src/withReactNativeEnrichedMarkdown.ts +0 -17
  505. package/plugin/tsconfig.build.json +0 -16
@@ -9,29 +9,44 @@ import android.util.AttributeSet
9
9
  import android.util.Log
10
10
  import android.view.View
11
11
  import android.widget.FrameLayout
12
+ import com.facebook.react.bridge.Arguments
12
13
  import com.facebook.react.bridge.ReadableMap
14
+ import com.facebook.react.uimanager.StateWrapper
13
15
  import com.swmansion.enriched.markdown.accessibility.AccessibilityLabels
14
16
  import com.swmansion.enriched.markdown.parser.Md4cFlags
15
17
  import com.swmansion.enriched.markdown.parser.Parser
16
18
  import com.swmansion.enriched.markdown.spoiler.SpoilerOverlay
17
19
  import com.swmansion.enriched.markdown.styles.StyleConfig
18
20
  import com.swmansion.enriched.markdown.utils.common.BreakStrategyUtils
21
+ import com.swmansion.enriched.markdown.utils.common.CodeBlockStreamingMode
19
22
  import com.swmansion.enriched.markdown.utils.common.FeatureFlags
20
23
  import com.swmansion.enriched.markdown.utils.common.MarkdownSegmentRenderer
21
24
  import com.swmansion.enriched.markdown.utils.common.RenderedSegment
22
25
  import com.swmansion.enriched.markdown.utils.common.SegmentReconciler
23
26
  import com.swmansion.enriched.markdown.utils.common.StreamingMarkdownFilter
24
27
  import com.swmansion.enriched.markdown.utils.common.TableStreamingMode
28
+ import com.swmansion.enriched.markdown.utils.common.emitLinkLayout
29
+ import com.swmansion.enriched.markdown.utils.common.emitSelectionChange
25
30
  import com.swmansion.enriched.markdown.utils.common.isReducedMotionEnabled
26
31
  import com.swmansion.enriched.markdown.utils.common.splitASTIntoSegments
27
32
  import com.swmansion.enriched.markdown.utils.text.TailFadeInAnimator
33
+ import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutCollector
34
+ import com.swmansion.enriched.markdown.utils.text.view.LinkLayoutFragment
35
+ import com.swmansion.enriched.markdown.utils.text.view.SelectionChangeReporter
28
36
  import com.swmansion.enriched.markdown.utils.text.view.SelectionMenuConfig
37
+ import com.swmansion.enriched.markdown.utils.text.view.SelectionRect
38
+ import com.swmansion.enriched.markdown.utils.text.view.SelectionSnapshot
29
39
  import com.swmansion.enriched.markdown.utils.text.view.applySelectionColors
40
+ import com.swmansion.enriched.markdown.utils.text.view.currentTextSelection
41
+ import com.swmansion.enriched.markdown.views.BlockMenuOptions
30
42
  import com.swmansion.enriched.markdown.views.BlockSegmentView
43
+ import com.swmansion.enriched.markdown.views.CodeBlockContainerView
31
44
  import com.swmansion.enriched.markdown.views.TableContainerView
32
45
  import java.util.EnumSet
33
46
  import java.util.concurrent.ExecutorService
34
47
  import java.util.concurrent.Executors
48
+ import kotlin.math.ceil
49
+ import kotlin.math.max
35
50
 
36
51
  class EnrichedMarkdown
37
52
  @JvmOverloads
@@ -62,7 +77,12 @@ class EnrichedMarkdown
62
77
  private val dirtyFlags = EnumSet.noneOf(DirtyFlag::class.java)
63
78
  var streamingAnimation: Boolean = false
64
79
 
80
+ // used to force a Yoga re-measure when a block image resolves its box height
81
+ var stateWrapper: StateWrapper? = null
82
+ private var forceHeightRecalculationCounter = 0
83
+
65
84
  var tableStreamingMode: TableStreamingMode = TableStreamingMode.PROGRESSIVE
85
+ var codeBlockStreamingMode: CodeBlockStreamingMode = CodeBlockStreamingMode.PROGRESSIVE
66
86
  private var renderPending: Boolean = false
67
87
 
68
88
  var currentMarkdown: String = ""
@@ -79,18 +99,39 @@ class EnrichedMarkdown
79
99
  private var allowFontScaling: Boolean = true
80
100
  private var maxFontSizeMultiplier: Float = 0f
81
101
  private var allowTrailingMargin: Boolean = false
102
+ private var imageRequestHeaders: Map<String, String> = emptyMap()
82
103
  private var selectable: Boolean = true
104
+ private var contextMenuHidden: Boolean = false
83
105
  private var selectionColor: Int? = null
84
106
  private var selectionHandleColor: Int? = null
85
107
  private var selectionMenuConfig = SelectionMenuConfig()
86
108
  private var accessibilityLabels = AccessibilityLabels()
87
109
  private var textBreakStrategy: String = BreakStrategyUtils.DEFAULT_STRATEGY
110
+ private var linkLayoutEnabled = false
111
+ private var lastLinkLayout: List<LinkLayoutFragment>? = null
112
+
113
+ // At most one segment holds a selection; a new one clears the previous.
114
+ private var selectionOwner: EnrichedMarkdownInternalText? = null
115
+
116
+ // A block long-pressed while contextMenuHidden is on, reported as the selection.
117
+ private class ActiveBlock(
118
+ val view: View,
119
+ val source: String,
120
+ val markdown: String,
121
+ val text: String,
122
+ )
123
+
124
+ private var activeBlock: ActiveBlock? = null
125
+ private val selectionReporter =
126
+ SelectionChangeReporter(this, ::selectionSnapshot) { snapshot -> emitSelectionChange(this, snapshot) }
88
127
 
89
128
  private var onLinkPressCallback: ((String) -> Unit)? = null
90
129
  private var onLinkLongPressCallback: ((String) -> Unit)? = null
91
130
  private var onTaskListItemPressCallback: ((Int, Boolean, String) -> Unit)? = null
131
+ private var onCopyPressCallback: ((String, String) -> Unit)? = null
92
132
  private var contextMenuItemTexts: List<String> = emptyList()
93
133
  var onContextMenuItemPressCallback: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
134
+ var onCopyRequestCallback: ((source: String, markdown: String, text: String, whole: Boolean) -> Unit)? = null
94
135
  var spoilerOverlay: SpoilerOverlay = SpoilerOverlay.PARTICLES
95
136
  set(value) {
96
137
  if (field == value) return
@@ -99,6 +140,14 @@ class EnrichedMarkdown
99
140
  it.spoilerOverlay = value
100
141
  }
101
142
  }
143
+ var enableTaskListItemToggle: Boolean = true
144
+ set(value) {
145
+ if (field == value) return
146
+ field = value
147
+ segmentViews.filterIsInstance<EnrichedMarkdownInternalText>().forEach {
148
+ it.enableTaskListItemToggle = value
149
+ }
150
+ }
102
151
 
103
152
  fun setMarkdownContent(markdown: String) {
104
153
  if (currentMarkdown == markdown) return
@@ -109,6 +158,7 @@ class EnrichedMarkdown
109
158
  fun setMarkdownStyle(style: ReadableMap?) {
110
159
  markdownStyleMap = style
111
160
  val newConfig = style?.let { StyleConfig(it, context, allowFontScaling, maxFontSizeMultiplier) }
161
+ newConfig?.imageRequestHeaders = imageRequestHeaders
112
162
  if (markdownStyle == newConfig) return
113
163
  markdownStyle = newConfig
114
164
  dirtyFlags += DirtyFlag.RECREATE_SEGMENTS
@@ -116,8 +166,18 @@ class EnrichedMarkdown
116
166
  renderPending = true
117
167
  }
118
168
 
169
+ fun setImageRequestHeaders(headers: Map<String, String>) {
170
+ if (imageRequestHeaders == headers) return
171
+ imageRequestHeaders = headers
172
+ markdownStyle?.imageRequestHeaders = headers
173
+ dirtyFlags += DirtyFlag.RECREATE_SEGMENTS
174
+ renderPending = true
175
+ }
176
+
119
177
  fun commitProps() {
120
178
  MeasurementStore.updateStreamingTableMode(id, tableStreamingMode)
179
+ MeasurementStore.updateStreamingCodeBlockMode(id, codeBlockStreamingMode)
180
+ MeasurementStore.updateFontScalingSettings(id, allowFontScaling, maxFontSizeMultiplier)
121
181
  if (renderPending) {
122
182
  renderPending = false
123
183
  scheduleRenderIfNeeded()
@@ -146,6 +206,7 @@ class EnrichedMarkdown
146
206
  fun setAllowFontScaling(allow: Boolean) {
147
207
  if (allowFontScaling == allow) return
148
208
  allowFontScaling = allow
209
+ MeasurementStore.updateFontScalingSettings(id, allowFontScaling, maxFontSizeMultiplier)
149
210
  recreateStyleConfig()
150
211
  dirtyFlags += DirtyFlag.RECREATE_SEGMENTS
151
212
  dirtyFlags += DirtyFlag.FORCE_HEIGHT
@@ -155,6 +216,7 @@ class EnrichedMarkdown
155
216
  fun setMaxFontSizeMultiplier(multiplier: Float) {
156
217
  if (maxFontSizeMultiplier == multiplier) return
157
218
  maxFontSizeMultiplier = multiplier
219
+ MeasurementStore.updateFontScalingSettings(id, allowFontScaling, maxFontSizeMultiplier)
158
220
  recreateStyleConfig()
159
221
  dirtyFlags += DirtyFlag.RECREATE_SEGMENTS
160
222
  dirtyFlags += DirtyFlag.FORCE_HEIGHT
@@ -177,6 +239,42 @@ class EnrichedMarkdown
177
239
  }
178
240
  }
179
241
 
242
+ fun setContextMenuHidden(value: Boolean) {
243
+ if (contextMenuHidden == value) return
244
+ contextMenuHidden = value
245
+ applyContextMenuHiddenToSegments()
246
+ }
247
+
248
+ /**
249
+ * Fans the flag out to every segment that owns a copy menu: text segments
250
+ * (floating ActionMode toolbar), tables, code blocks and math blocks (all use
251
+ * their own long-press popup, so none is covered by the ActionMode callback).
252
+ */
253
+ private fun applyContextMenuHiddenToSegments() {
254
+ segmentViews.forEach { view ->
255
+ when {
256
+ view is EnrichedMarkdownInternalText -> view.contextMenuHidden = contextMenuHidden
257
+ view is TableContainerView -> view.contextMenuHidden = contextMenuHidden
258
+ view is CodeBlockContainerView -> view.contextMenuHidden = contextMenuHidden
259
+ isMathContainerView(view) -> setMathContextMenuHidden(view)
260
+ }
261
+ }
262
+ }
263
+
264
+ /**
265
+ * MathContainerView lives in the optional `math` source set, so it is only
266
+ * reachable reflectively. The Kotlin `var` compiles to a setter taking a
267
+ * primitive boolean — `Boolean::class.java` would resolve to
268
+ * `java.lang.Boolean` and silently miss.
269
+ */
270
+ private fun setMathContextMenuHidden(view: View) {
271
+ runCatching {
272
+ view.javaClass
273
+ .getMethod("setContextMenuHidden", Boolean::class.javaPrimitiveType)
274
+ .invoke(view, contextMenuHidden)
275
+ }.onFailure { Log.e(TAG, "Failed to set contextMenuHidden on math view", it) }
276
+ }
277
+
180
278
  fun setSelectionColor(color: Int?) {
181
279
  if (selectionColor == color) return
182
280
  selectionColor = color
@@ -221,6 +319,10 @@ class EnrichedMarkdown
221
319
  onTaskListItemPressCallback = callback
222
320
  }
223
321
 
322
+ fun setOnCopyPressCallback(callback: ((code: String, language: String) -> Unit)?) {
323
+ onCopyPressCallback = callback
324
+ }
325
+
224
326
  fun setContextMenuItems(items: List<String>) {
225
327
  contextMenuItemTexts = items
226
328
  segmentViews.filterIsInstance<EnrichedMarkdownInternalText>().forEach {
@@ -234,8 +336,8 @@ class EnrichedMarkdown
234
336
  segmentViews.filterIsInstance<EnrichedMarkdownInternalText>().forEach {
235
337
  it.selectionMenuConfig = config
236
338
  }
237
- // Table and math views cache the copy labels, so re-push them on update
238
- // (e.g. a language change without a remount) to avoid stale labels.
339
+ // Block views cache the copy labels and menu options, so re-push them on
340
+ // update (e.g. a language change without a remount) to avoid stale values.
239
341
  pushCopyLabelsToBlockSegments()
240
342
  }
241
343
 
@@ -247,6 +349,13 @@ class EnrichedMarkdown
247
349
  view is TableContainerView -> {
248
350
  view.copyLabel = copyLabel
249
351
  view.copyAsMarkdownLabel = copyAsMarkdownLabel
352
+ view.menuOptions = blockMenuOptions("table")
353
+ }
354
+
355
+ view is CodeBlockContainerView -> {
356
+ view.copyLabel = copyLabel
357
+ view.copyAsMarkdownLabel = copyAsMarkdownLabel
358
+ view.menuOptions = blockMenuOptions("code")
250
359
  }
251
360
 
252
361
  isMathContainerView(view) -> {
@@ -255,6 +364,9 @@ class EnrichedMarkdown
255
364
  view.javaClass
256
365
  .getMethod("setCopyAsMarkdownLabel", String::class.java)
257
366
  .invoke(view, copyAsMarkdownLabel)
367
+ view.javaClass
368
+ .getMethod("setMenuOptions", BlockMenuOptions::class.java)
369
+ .invoke(view, blockMenuOptions("math"))
258
370
  }
259
371
  }
260
372
  }
@@ -281,6 +393,22 @@ class EnrichedMarkdown
281
393
  }
282
394
  }
283
395
 
396
+ private fun blockMenuOptions(source: String) =
397
+ BlockMenuOptions.from(selectionMenuConfig) { markdown, text ->
398
+ onCopyRequestCallback?.invoke(source, markdown, text, true)
399
+ }
400
+
401
+ // Segments only see their own text, so a selection is the whole component
402
+ // only when it spans the sole segment; the markdown is then the original.
403
+ private fun forwardSegmentCopyRequest(
404
+ markdown: String,
405
+ text: String,
406
+ fullSelection: Boolean,
407
+ ) {
408
+ val whole = fullSelection && segmentViews.size == 1 && currentMarkdown.isNotEmpty()
409
+ onCopyRequestCallback?.invoke("selection", if (whole) currentMarkdown else markdown, text, whole)
410
+ }
411
+
284
412
  private fun forwardContextMenuItemPress(
285
413
  itemText: String,
286
414
  selectedText: String,
@@ -290,9 +418,84 @@ class EnrichedMarkdown
290
418
  onContextMenuItemPressCallback?.invoke(itemText, selectedText, selectionStart, selectionEnd)
291
419
  }
292
420
 
421
+ fun setEmitSelectionChange(enabled: Boolean) {
422
+ selectionReporter.enabled = enabled
423
+ }
424
+
425
+ /** Clears the selection of every segment (and any active block) and reports `active: false`. */
426
+ fun clearSelection() {
427
+ val owner = selectionOwner
428
+ selectionOwner = null
429
+ activeBlock = null
430
+ owner?.clearSelection()
431
+ selectionReporter.flushNow()
432
+ }
433
+
434
+ private fun onSegmentSelectionChanged(
435
+ view: EnrichedMarkdownInternalText,
436
+ hasSelection: Boolean,
437
+ ) {
438
+ if (hasSelection) {
439
+ // Take ownership first so the previous owner's own "cleared" callback is ignored.
440
+ val previous = selectionOwner
441
+ selectionOwner = view
442
+ activeBlock = null
443
+ if (previous !== view) previous?.clearSelection()
444
+ selectionReporter.schedule()
445
+ } else if (selectionOwner === view) {
446
+ selectionReporter.onSelectionChanged(false)
447
+ }
448
+ }
449
+
450
+ private fun onBlockMenuSuppressed(
451
+ view: View,
452
+ source: String,
453
+ markdown: String,
454
+ text: String,
455
+ ) {
456
+ if (!selectionReporter.enabled) return
457
+ val previous = selectionOwner
458
+ selectionOwner = null
459
+ previous?.clearSelection()
460
+ activeBlock = ActiveBlock(view, source, markdown, text)
461
+ selectionReporter.forgetLastReported()
462
+ selectionReporter.flushNow()
463
+ }
464
+
465
+ private fun selectionSnapshot(): SelectionSnapshot {
466
+ activeBlock?.let { block ->
467
+ if (block.view.parent !== this) return SelectionSnapshot.INACTIVE
468
+ val rect =
469
+ SelectionRect.fromPx(
470
+ block.view.left.toFloat(),
471
+ block.view.top.toFloat(),
472
+ block.view.right.toFloat(),
473
+ block.view.bottom.toFloat(),
474
+ )
475
+ return SelectionSnapshot.block(block.source, block.markdown, block.text, rect)
476
+ }
477
+ val owner = selectionOwner?.takeIf { it.parent === this } ?: return SelectionSnapshot.INACTIVE
478
+ val selection = owner.currentTextSelection(owner.left, owner.top) ?: return SelectionSnapshot.INACTIVE
479
+ // Same rule as forwardSegmentCopyRequest.
480
+ val whole = selection.fullSelection && segmentViews.size == 1 && currentMarkdown.isNotEmpty()
481
+ return SelectionSnapshot(
482
+ active = true,
483
+ source = "selection",
484
+ text = selection.text,
485
+ markdown = if (whole) currentMarkdown else selection.markdown,
486
+ whole = whole,
487
+ rect = selection.rect,
488
+ startRect = selection.startRect,
489
+ endRect = selection.endRect,
490
+ )
491
+ }
492
+
293
493
  private fun recreateStyleConfig() {
294
494
  markdownStyleMap?.let {
295
- markdownStyle = StyleConfig(it, context, allowFontScaling, maxFontSizeMultiplier)
495
+ markdownStyle =
496
+ StyleConfig(it, context, allowFontScaling, maxFontSizeMultiplier).also { config ->
497
+ config.imageRequestHeaders = imageRequestHeaders
498
+ }
296
499
  }
297
500
  }
298
501
 
@@ -305,26 +508,29 @@ class EnrichedMarkdown
305
508
  val markdown = currentMarkdown.takeIf { it.isNotEmpty() } ?: return
306
509
  val isStreaming = streamingAnimation
307
510
  val tableMode = tableStreamingMode
511
+ val codeBlockMode = codeBlockStreamingMode
308
512
 
309
513
  val renderId = ++currentRenderId
310
514
 
311
515
  executor.execute {
312
516
  try {
313
- val renderableMarkdown =
517
+ val filtered =
314
518
  if (isStreaming) {
315
- StreamingMarkdownFilter.renderableMarkdownForStreaming(markdown, tableMode)
519
+ StreamingMarkdownFilter.renderableMarkdownForStreaming(markdown, tableMode, codeBlockMode)
316
520
  } else {
317
- markdown
521
+ null
318
522
  }
523
+ val renderableMarkdown = filtered?.markdown ?: markdown
524
+ val hasPendingCodeBlock = filtered?.endsInsideOpenCodeFence ?: false
319
525
 
320
526
  if (renderableMarkdown.isEmpty()) {
321
- postToMain(renderId) { applyRenderedSegments(emptyList(), style) }
527
+ postToMain(renderId) { applyRenderedSegments(emptyList(), style, false) }
322
528
  return@execute
323
529
  }
324
530
 
325
531
  val ast =
326
532
  parser.parseMarkdown(renderableMarkdown, md4cFlags) ?: run {
327
- postToMain(renderId) { applyRenderedSegments(emptyList(), style) }
533
+ postToMain(renderId) { applyRenderedSegments(emptyList(), style, false) }
328
534
  return@execute
329
535
  }
330
536
 
@@ -338,18 +544,25 @@ class EnrichedMarkdown
338
544
  onLinkLongPressCallback,
339
545
  )
340
546
 
341
- postToMain(renderId) { applyRenderedSegments(renderedSegments, style) }
547
+ postToMain(renderId) { applyRenderedSegments(renderedSegments, style, hasPendingCodeBlock) }
342
548
  } catch (e: Exception) {
343
549
  Log.e(TAG, "Render failed", e)
344
- postToMain(renderId) { applyRenderedSegments(emptyList(), style) }
550
+ postToMain(renderId) { applyRenderedSegments(emptyList(), style, false) }
345
551
  }
346
552
  }
347
553
  }
348
554
 
555
+ // Trailing code block whose closing fence hasn't streamed in yet, if any.
556
+ private var pendingCodeBlockSegment: RenderedSegment.CodeBlock? = null
557
+
349
558
  private fun applyRenderedSegments(
350
559
  renderedSegments: List<RenderedSegment>,
351
560
  style: StyleConfig,
561
+ hasPendingCodeBlock: Boolean,
352
562
  ) {
563
+ pendingCodeBlockSegment =
564
+ if (hasPendingCodeBlock) renderedSegments.lastOrNull() as? RenderedSegment.CodeBlock else null
565
+
353
566
  val reset = DirtyFlag.RECREATE_SEGMENTS in dirtyFlags
354
567
  val forceHeight = DirtyFlag.FORCE_HEIGHT in dirtyFlags
355
568
  dirtyFlags.clear()
@@ -377,6 +590,17 @@ class EnrichedMarkdown
377
590
  segmentSignatures.clear()
378
591
  segmentSignatures.addAll(result.signatures)
379
592
 
593
+ if (selectionOwner?.let { owner -> segmentViews.none { it === owner } } == true) selectionOwner = null
594
+ if (activeBlock?.let { block -> segmentViews.none { it === block.view } } == true) activeBlock = null
595
+
596
+ // A just-closed block has unchanged content, so the reconciler reuses it
597
+ // without an update; sync pending here to trigger its deferred highlight.
598
+ segmentViews.forEachIndexed { index, view ->
599
+ if (view is CodeBlockContainerView) {
600
+ view.pending = pendingCodeBlockSegment != null && index == segmentViews.size - 1
601
+ }
602
+ }
603
+
380
604
  val topologyChanged = result.viewsToAttach.isNotEmpty() || result.viewsToRemove.isNotEmpty()
381
605
 
382
606
  if (width > 0) {
@@ -399,6 +623,7 @@ class EnrichedMarkdown
399
623
  is RenderedSegment.Text -> view is EnrichedMarkdownInternalText
400
624
  is RenderedSegment.Table -> view is TableContainerView
401
625
  is RenderedSegment.Math -> isMathContainerView(view)
626
+ is RenderedSegment.CodeBlock -> view is CodeBlockContainerView
402
627
  }
403
628
 
404
629
  private fun isMathContainerView(view: View): Boolean = mathContainerClass?.isInstance(view) == true
@@ -411,6 +636,7 @@ class EnrichedMarkdown
411
636
  is RenderedSegment.Text -> createTextView(segment)
412
637
  is RenderedSegment.Table -> createTableView(segment, style)
413
638
  is RenderedSegment.Math -> createMathView(segment, style)
639
+ is RenderedSegment.CodeBlock -> createCodeBlockView(segment, style)
414
640
  }
415
641
 
416
642
  private fun updateSegmentView(
@@ -441,6 +667,13 @@ class EnrichedMarkdown
441
667
  ?.getMethod("applyLatex", String::class.java)
442
668
  ?.invoke(view, segment.latex)
443
669
  }
670
+
671
+ is RenderedSegment.CodeBlock -> {
672
+ (view as CodeBlockContainerView).apply {
673
+ pending = segment === pendingCodeBlockSegment
674
+ applyCodeBlockNode(segment.node)
675
+ }
676
+ }
444
677
  }
445
678
  }
446
679
 
@@ -451,7 +684,7 @@ class EnrichedMarkdown
451
684
  if (!streamingAnimation) return
452
685
  when (segment) {
453
686
  is RenderedSegment.Text -> animateTextViewTail(view as EnrichedMarkdownInternalText, 0)
454
- is RenderedSegment.Table, is RenderedSegment.Math -> animateBlockViewFadeIn(view)
687
+ is RenderedSegment.Table, is RenderedSegment.Math, is RenderedSegment.CodeBlock -> animateBlockViewFadeIn(view)
455
688
  }
456
689
  }
457
690
 
@@ -479,9 +712,12 @@ class EnrichedMarkdown
479
712
  private fun createTextView(segment: RenderedSegment.Text) =
480
713
  EnrichedMarkdownInternalText(context).apply {
481
714
  spoilerOverlay = this@EnrichedMarkdown.spoilerOverlay
715
+ enableTaskListItemToggle = this@EnrichedMarkdown.enableTaskListItemToggle
482
716
  selectionMenuConfig = this@EnrichedMarkdown.selectionMenuConfig
717
+ contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
483
718
  accessibilityLabels = this@EnrichedMarkdown.accessibilityLabels
484
719
  setIsSelectable(selectable)
720
+ markdownStyle?.paragraphStyle?.fontSize?.let { setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, it) }
485
721
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
486
722
  breakStrategy = BreakStrategyUtils.resolveBreakStrategy(textBreakStrategy)
487
723
  }
@@ -495,6 +731,8 @@ class EnrichedMarkdown
495
731
  onTaskListItemPressCallback = { taskIndex, checked, itemText ->
496
732
  this@EnrichedMarkdown.onTaskListItemPressCallback?.invoke(taskIndex, checked, itemText)
497
733
  }
734
+ onCopyRequest = ::forwardSegmentCopyRequest
735
+ onSelectionChangedCallback = ::onSegmentSelectionChanged
498
736
 
499
737
  if (contextMenuItemTexts.isNotEmpty()) {
500
738
  setContextMenuItems(contextMenuItemTexts, ::forwardContextMenuItemPress)
@@ -514,9 +752,28 @@ class EnrichedMarkdown
514
752
  onLinkLongPress = onLinkLongPressCallback
515
753
  copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
516
754
  copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
755
+ contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
756
+ menuOptions = blockMenuOptions("table")
757
+ onMenuSuppressed = { markdown, text -> onBlockMenuSuppressed(this, "table", markdown, text) }
517
758
  applyTableNode(segment.node)
518
759
  }
519
760
 
761
+ private fun createCodeBlockView(
762
+ segment: RenderedSegment.CodeBlock,
763
+ style: StyleConfig,
764
+ ) = CodeBlockContainerView(context, style).apply {
765
+ copyLabel = this@EnrichedMarkdown.selectionMenuConfig.copyLabel
766
+ copyAsMarkdownLabel = this@EnrichedMarkdown.selectionMenuConfig.copyAsMarkdownLabel
767
+ contextMenuHidden = this@EnrichedMarkdown.contextMenuHidden
768
+ menuOptions = blockMenuOptions("code")
769
+ onMenuSuppressed = { markdown, text -> onBlockMenuSuppressed(this, "code", markdown, text) }
770
+ onCopyPress = { code, language ->
771
+ this@EnrichedMarkdown.onCopyPressCallback?.invoke(code, language)
772
+ }
773
+ pending = segment === pendingCodeBlockSegment
774
+ applyCodeBlockNode(segment.node)
775
+ }
776
+
520
777
  private fun createMathView(
521
778
  segment: RenderedSegment.Math,
522
779
  style: StyleConfig,
@@ -539,6 +796,16 @@ class EnrichedMarkdown
539
796
  resolvedClass
540
797
  .getMethod("setCopyAsMarkdownLabel", String::class.java)
541
798
  .invoke(view, selectionMenuConfig.copyAsMarkdownLabel)
799
+ resolvedClass
800
+ .getMethod("setMenuOptions", BlockMenuOptions::class.java)
801
+ .invoke(view, blockMenuOptions("math"))
802
+ setMathContextMenuHidden(view)
803
+ val onMenuSuppressed: (String, String) -> Unit = { markdown, text ->
804
+ onBlockMenuSuppressed(view, "math", markdown, text)
805
+ }
806
+ runCatching {
807
+ resolvedClass.getMethod("setOnMenuSuppressed", Function2::class.java).invoke(view, onMenuSuppressed)
808
+ }.onFailure { Log.e(TAG, "Failed to set onMenuSuppressed on math view", it) }
542
809
  resolvedClass.getMethod("applyLatex", String::class.java).invoke(view, segment.latex)
543
810
  view
544
811
  } catch (e: Exception) {
@@ -566,10 +833,31 @@ class EnrichedMarkdown
566
833
  layoutSegments()
567
834
  }
568
835
 
836
+ fun onImageLayoutChanged() {
837
+ if (width > 0) {
838
+ layoutSegments()
839
+ requestLayout()
840
+ }
841
+ MeasurementStore.invalidate(id)
842
+ val wrapper = stateWrapper ?: return
843
+ val state = Arguments.createMap()
844
+ state.putInt("forceHeightRecalculationCounter", ++forceHeightRecalculationCounter)
845
+ wrapper.updateState(state)
846
+ }
847
+
569
848
  private fun layoutSegments() {
570
849
  val containerWidth = width
571
850
  if (containerWidth <= 0) return
572
851
 
852
+ val needsOverhang =
853
+ segmentViews.any { view ->
854
+ view is TableContainerView &&
855
+ ceil(view.tableStyle.horizontalOverflow.toDouble()).toInt() > 0
856
+ }
857
+ if (clipChildren == needsOverhang) {
858
+ clipChildren = !needsOverhang
859
+ }
860
+
573
861
  var currentY = 0
574
862
  val lastIndex = segmentViews.lastIndex
575
863
  val widthSpec = MeasureSpec.makeMeasureSpec(containerWidth, MeasureSpec.EXACTLY)
@@ -580,15 +868,64 @@ class EnrichedMarkdown
580
868
  val shouldAddBottomMargin = index != lastIndex || allowTrailingMargin
581
869
 
582
870
  currentY += segment?.segmentMarginTop ?: 0
583
- view.measure(widthSpec, heightSpec)
584
871
 
585
- view.layout(0, currentY, containerWidth, currentY + view.measuredHeight)
872
+ val overhang =
873
+ if (view is TableContainerView) {
874
+ max(ceil(view.tableStyle.horizontalOverflow.toDouble()).toInt(), 0)
875
+ } else {
876
+ 0
877
+ }
878
+
879
+ if (overhang > 0) {
880
+ val extendedWidth = containerWidth + overhang * 2
881
+ val extWidthSpec = MeasureSpec.makeMeasureSpec(extendedWidth, MeasureSpec.EXACTLY)
882
+ view.measure(extWidthSpec, heightSpec)
883
+ view.layout(-overhang, currentY, containerWidth + overhang, currentY + view.measuredHeight)
884
+ } else {
885
+ view.measure(widthSpec, heightSpec)
886
+ view.layout(0, currentY, containerWidth, currentY + view.measuredHeight)
887
+ }
586
888
  currentY += view.measuredHeight
587
889
 
588
890
  if (shouldAddBottomMargin) {
589
891
  currentY += segment?.segmentMarginBottom ?: 0
590
892
  }
591
893
  }
894
+
895
+ emitLinkLayoutIfNeeded()
896
+ // Segments may have moved or been replaced; re-read the selection (deduplicated).
897
+ if (selectionOwner != null || activeBlock != null) selectionReporter.schedule() else selectionReporter.flushNow()
898
+ }
899
+
900
+ fun setEmitLinkLayout(enabled: Boolean) {
901
+ if (linkLayoutEnabled == enabled) return
902
+ linkLayoutEnabled = enabled
903
+ lastLinkLayout = null
904
+ emitLinkLayoutIfNeeded()
905
+ }
906
+
907
+ /**
908
+ * Reports link rects of every text segment (offset by the segment's position) to JS for
909
+ * `renderLinkDecoration`. Tables are skipped: their cells scroll horizontally, so rects would
910
+ * go stale. Runs after each segment layout pass; deduplicated so an unchanged layout sends nothing.
911
+ */
912
+ private fun emitLinkLayoutIfNeeded() {
913
+ if (!linkLayoutEnabled || width <= 0) return
914
+ val fragments = mutableListOf<LinkLayoutFragment>()
915
+ var linkCount = 0
916
+ segmentViews.forEach { view ->
917
+ if (view is TableContainerView) {
918
+ linkCount += view.linkCount
919
+ return@forEach
920
+ }
921
+ if (view !is EnrichedMarkdownInternalText) return@forEach
922
+ val (segmentFragments, segmentLinks) = LinkLayoutCollector.collect(view, view.left, view.top, linkCount)
923
+ fragments += segmentFragments
924
+ linkCount += segmentLinks
925
+ }
926
+ if (fragments == lastLinkLayout) return
927
+ lastLinkLayout = fragments
928
+ emitLinkLayout(this, fragments)
592
929
  }
593
930
 
594
931
  private fun computeSegmentsTotalHeight(): Int {
@@ -606,6 +943,7 @@ class EnrichedMarkdown
606
943
  }
607
944
 
608
945
  fun cleanup() {
946
+ selectionReporter.cancel()
609
947
  executor.shutdownNow()
610
948
  }
611
949