@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.9

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 (358) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +140 -3
  3. package/ReactNativeRenderRichText.podspec +23 -0
  4. package/android/build.gradle +86 -0
  5. package/android/consumer-rules.pro +3 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/jni-adapter.cpp +191 -0
  8. package/android/src/main/java/com/renderrichtext/EnrichedTiptapText.kt +673 -0
  9. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -0
  10. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextManager.kt +405 -0
  11. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -0
  12. package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -0
  13. package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -0
  14. package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -0
  15. package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -0
  16. package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -0
  17. package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -0
  18. package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -0
  19. package/android/src/main/java/com/renderrichtext/events/FactoidPressEvent.kt +23 -0
  20. package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -0
  21. package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -0
  22. package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -0
  23. package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -0
  24. package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -0
  25. package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -0
  26. package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -0
  27. package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -0
  28. package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -0
  29. package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -0
  30. package/android/src/main/java/com/renderrichtext/parser/TiptapASTNode.kt +69 -0
  31. package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -0
  32. package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -0
  33. package/android/src/main/java/com/renderrichtext/renderer/BlockStyleContext.kt +181 -0
  34. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -0
  35. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -0
  36. package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -0
  37. package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -0
  38. package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -0
  39. package/android/src/main/java/com/renderrichtext/renderer/FactoidRenderer.kt +97 -0
  40. package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -0
  41. package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -0
  42. package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -0
  43. package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -0
  44. package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -0
  45. package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -0
  46. package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -0
  47. package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -0
  48. package/android/src/main/java/com/renderrichtext/renderer/NodeRenderer.kt +207 -0
  49. package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -0
  50. package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -0
  51. package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -0
  52. package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -0
  53. package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -0
  54. package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -0
  55. package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -0
  56. package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -0
  57. package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -0
  58. package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -0
  59. package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -0
  60. package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -0
  61. package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -0
  62. package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -0
  63. package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -0
  64. package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -0
  65. package/android/src/main/java/com/renderrichtext/spans/FactoidBackgroundSpan.kt +265 -0
  66. package/android/src/main/java/com/renderrichtext/spans/FactoidPaddingSpan.kt +258 -0
  67. package/android/src/main/java/com/renderrichtext/spans/FactoidSpan.kt +32 -0
  68. package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -0
  69. package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -0
  70. package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -0
  71. package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -0
  72. package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -0
  73. package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -0
  74. package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -0
  75. package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -0
  76. package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -0
  77. package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -0
  78. package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -0
  79. package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -0
  80. package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -0
  81. package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -0
  82. package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -0
  83. package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -0
  84. package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -0
  85. package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -0
  86. package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -0
  87. package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -0
  88. package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -0
  89. package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -0
  90. package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -0
  91. package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -0
  92. package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -0
  93. package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -0
  94. package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -0
  95. package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -0
  96. package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -0
  97. package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -0
  98. package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -0
  99. package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -0
  100. package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -0
  101. package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -0
  102. package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -0
  103. package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -0
  104. package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -0
  105. package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -0
  106. package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -0
  107. package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -0
  108. package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -0
  109. package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -0
  110. package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -0
  111. package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -0
  112. package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -0
  113. package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -0
  114. package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -0
  115. package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -0
  116. package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -0
  117. package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -0
  118. package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -0
  119. package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -0
  120. package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -0
  121. package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -0
  122. package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -0
  123. package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -0
  124. package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -0
  125. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -0
  126. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -0
  127. package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -0
  128. package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -0
  129. package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -0
  130. package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -0
  131. package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -0
  132. package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -0
  133. package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -0
  134. package/android/src/main/java/com/renderrichtext/utils/common/TiptapViewManagerUtils.kt +242 -0
  135. package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -0
  136. package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -0
  137. package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -0
  138. package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -0
  139. package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -0
  140. package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -0
  141. package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -0
  142. package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -0
  143. package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -0
  144. package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -0
  145. package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -0
  146. package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -0
  147. package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -0
  148. package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -0
  149. package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -0
  150. package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -0
  151. package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -0
  152. package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -0
  153. package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -0
  154. package/android/src/main/java/com/renderrichtext/utils/text/view/LinkEvents.kt +88 -0
  155. package/android/src/main/java/com/renderrichtext/utils/text/view/LinkLongPressMovementMethod.kt +449 -0
  156. package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -0
  157. package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -0
  158. package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -0
  159. package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -0
  160. package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -0
  161. package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -0
  162. package/android/src/main/jni/CMakeLists.txt +52 -0
  163. package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -0
  164. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -0
  165. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -0
  166. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -0
  167. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -0
  168. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -0
  169. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -0
  170. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -0
  171. package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -0
  172. package/cpp/parser/TiptapASTNode.hpp +68 -0
  173. package/cpp/parser/TiptapParser.cpp +233 -0
  174. package/cpp/parser/TiptapParser.hpp +13 -0
  175. package/cpp/vendor/nlohmann/json.hpp +24765 -0
  176. package/ios/EnrichedTiptapText.h +27 -0
  177. package/ios/EnrichedTiptapText.mm +1222 -0
  178. package/ios/attachments/ENRMImageAttachment.h +51 -0
  179. package/ios/attachments/ENRMImageAttachment.m +398 -0
  180. package/ios/attachments/ENRMLinkPillAttachment.h +47 -0
  181. package/ios/attachments/ENRMLinkPillAttachment.m +324 -0
  182. package/ios/attachments/ENRMLinkPillIconCache.h +28 -0
  183. package/ios/attachments/ENRMLinkPillIconCache.m +181 -0
  184. package/ios/attachments/ENRMLinkPillText.h +27 -0
  185. package/ios/attachments/ENRMLinkPillText.m +118 -0
  186. package/ios/attachments/ENRMMathInlineAttachment.h +7 -0
  187. package/ios/attachments/ENRMMathInlineAttachment.m +4 -0
  188. package/ios/attachments/ThematicBreakAttachment.h +15 -0
  189. package/ios/attachments/ThematicBreakAttachment.m +33 -0
  190. package/ios/internals/ENRMViewFreeMeasurement.h +208 -0
  191. package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -0
  192. package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -0
  193. package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -0
  194. package/ios/internals/EnrichedTiptapTextState.h +24 -0
  195. package/ios/internals/MeasurementCache.h +308 -0
  196. package/ios/internals/ShadowMeasurementUtils.h +145 -0
  197. package/ios/parser/ENRMTextLinkAttributes.h +7 -0
  198. package/ios/parser/ENRMTiptapParser.h +33 -0
  199. package/ios/parser/ENRMTiptapParser.mm +73 -0
  200. package/ios/parser/TiptapASTNode.h +57 -0
  201. package/ios/parser/TiptapASTNode.m +52 -0
  202. package/ios/parser/TiptapParserBridge.mm +179 -0
  203. package/ios/renderer/AttributedRenderer.h +23 -0
  204. package/ios/renderer/AttributedRenderer.m +229 -0
  205. package/ios/renderer/BaseRenderer.h +33 -0
  206. package/ios/renderer/BaseRenderer.m +46 -0
  207. package/ios/renderer/BaselineShiftTextAttributes.h +10 -0
  208. package/ios/renderer/BaselineShiftTextAttributes.m +26 -0
  209. package/ios/renderer/BlankLineRenderer.h +4 -0
  210. package/ios/renderer/BlankLineRenderer.m +51 -0
  211. package/ios/renderer/BlockquoteRenderer.h +6 -0
  212. package/ios/renderer/BlockquoteRenderer.m +258 -0
  213. package/ios/renderer/CodeBlockRenderer.h +9 -0
  214. package/ios/renderer/CodeBlockRenderer.m +99 -0
  215. package/ios/renderer/CodeRenderer.h +9 -0
  216. package/ios/renderer/CodeRenderer.m +69 -0
  217. package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -0
  218. package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -0
  219. package/ios/renderer/EmphasisRenderer.h +5 -0
  220. package/ios/renderer/EmphasisRenderer.m +100 -0
  221. package/ios/renderer/FactoidRenderer.h +9 -0
  222. package/ios/renderer/FactoidRenderer.m +130 -0
  223. package/ios/renderer/HeadingRenderer.h +6 -0
  224. package/ios/renderer/HeadingRenderer.m +103 -0
  225. package/ios/renderer/HighlightRenderer.h +7 -0
  226. package/ios/renderer/HighlightRenderer.m +33 -0
  227. package/ios/renderer/LinkRenderer.h +6 -0
  228. package/ios/renderer/LinkRenderer.m +158 -0
  229. package/ios/renderer/ListItemRenderer.h +24 -0
  230. package/ios/renderer/ListItemRenderer.m +283 -0
  231. package/ios/renderer/ListRenderer.h +13 -0
  232. package/ios/renderer/ListRenderer.m +126 -0
  233. package/ios/renderer/MentionRenderer.h +9 -0
  234. package/ios/renderer/MentionRenderer.m +96 -0
  235. package/ios/renderer/NodeRenderer.h +8 -0
  236. package/ios/renderer/ParagraphRenderer.h +6 -0
  237. package/ios/renderer/ParagraphRenderer.m +85 -0
  238. package/ios/renderer/RenderContext.h +145 -0
  239. package/ios/renderer/RenderContext.m +403 -0
  240. package/ios/renderer/RendererFactory.h +12 -0
  241. package/ios/renderer/RendererFactory.m +158 -0
  242. package/ios/renderer/StrikethroughRenderer.h +5 -0
  243. package/ios/renderer/StrikethroughRenderer.m +30 -0
  244. package/ios/renderer/StrongRenderer.h +5 -0
  245. package/ios/renderer/StrongRenderer.m +88 -0
  246. package/ios/renderer/TextRenderer.h +6 -0
  247. package/ios/renderer/TextRenderer.m +18 -0
  248. package/ios/renderer/ThematicBreakRenderer.h +4 -0
  249. package/ios/renderer/ThematicBreakRenderer.m +42 -0
  250. package/ios/renderer/UnderlineRenderer.h +4 -0
  251. package/ios/renderer/UnderlineRenderer.m +41 -0
  252. package/ios/styles/ENRMFontSlot.h +12 -0
  253. package/ios/styles/ENRMFontSlot.m +24 -0
  254. package/ios/styles/StyleConfig.h +488 -0
  255. package/ios/styles/StyleConfig.mm +2820 -0
  256. package/ios/utils/AccessibilityInfo.h +35 -0
  257. package/ios/utils/AccessibilityInfo.m +24 -0
  258. package/ios/utils/BlockquoteBorder.h +26 -0
  259. package/ios/utils/BlockquoteBorder.m +294 -0
  260. package/ios/utils/CodeBackground.h +19 -0
  261. package/ios/utils/CodeBackground.m +197 -0
  262. package/ios/utils/CodeBlockBackground.h +17 -0
  263. package/ios/utils/CodeBlockBackground.m +103 -0
  264. package/ios/utils/ContextMenuUtils.h +76 -0
  265. package/ios/utils/ContextMenuUtils.mm +75 -0
  266. package/ios/utils/ENRMAccessibilityLabels.h +40 -0
  267. package/ios/utils/ENRMAccessibilityLabels.m +5 -0
  268. package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -0
  269. package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -0
  270. package/ios/utils/ENRMAtomicSize.h +65 -0
  271. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  272. package/ios/utils/ENRMCodeBlockContent.mm +89 -0
  273. package/ios/utils/ENRMFeatureFlags.h +25 -0
  274. package/ios/utils/ENRMImageDownloader.h +30 -0
  275. package/ios/utils/ENRMImageDownloader.m +168 -0
  276. package/ios/utils/ENRMLatexErrorCoordinator.h +27 -0
  277. package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -0
  278. package/ios/utils/ENRMLatexErrorReporting.h +13 -0
  279. package/ios/utils/ENRMLinkContextMenus.h +93 -0
  280. package/ios/utils/ENRMLinkContextMenus.m +178 -0
  281. package/ios/utils/ENRMLinkRegexConfig.h +30 -0
  282. package/ios/utils/ENRMLinkRegexConfig.mm +104 -0
  283. package/ios/utils/ENRMLinkRegexProps.h +23 -0
  284. package/ios/utils/ENRMLocalImageLoader.h +35 -0
  285. package/ios/utils/ENRMLocalImageLoader.m +111 -0
  286. package/ios/utils/ENRMMenuAction.h +27 -0
  287. package/ios/utils/ENRMMenuAction.m +33 -0
  288. package/ios/utils/ENRMSpoilerOverlayManager.h +15 -0
  289. package/ios/utils/ENRMSpoilerOverlayManager.m +19 -0
  290. package/ios/utils/ENRMSpoilerTapUtils.h +6 -0
  291. package/ios/utils/ENRMSpoilerTapUtils.m +11 -0
  292. package/ios/utils/ENRMTailFadeInAnimator.h +16 -0
  293. package/ios/utils/ENRMTailFadeInAnimator.m +161 -0
  294. package/ios/utils/ENRMTextHitTest.h +23 -0
  295. package/ios/utils/ENRMTextHitTest.m +20 -0
  296. package/ios/utils/ENRMTextInteractionUtils.h +20 -0
  297. package/ios/utils/ENRMTextInteractionUtils.m +15 -0
  298. package/ios/utils/ENRMTextRenderer.h +38 -0
  299. package/ios/utils/ENRMTextRenderer.m +54 -0
  300. package/ios/utils/ENRMTextViewSetup.h +132 -0
  301. package/ios/utils/ENRMUIKit.h +429 -0
  302. package/ios/utils/EditMenuUtils.h +105 -0
  303. package/ios/utils/EditMenuUtils.m +143 -0
  304. package/ios/utils/FactoidBackground.h +20 -0
  305. package/ios/utils/FactoidBackground.m +161 -0
  306. package/ios/utils/FontScaleObserver.h +10 -0
  307. package/ios/utils/FontScaleObserver.m +55 -0
  308. package/ios/utils/FontUtils.h +31 -0
  309. package/ios/utils/FontUtils.m +63 -0
  310. package/ios/utils/HTMLGenerator.h +25 -0
  311. package/ios/utils/HTMLGenerator.m +1041 -0
  312. package/ios/utils/HeightUpdateUtils.h +25 -0
  313. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  314. package/ios/utils/LastElementUtils.h +97 -0
  315. package/ios/utils/LinkContextMenuUtils.h +55 -0
  316. package/ios/utils/LinkPillContentUtils.h +46 -0
  317. package/ios/utils/LinkTapUtils.h +56 -0
  318. package/ios/utils/LinkTapUtils.m +139 -0
  319. package/ios/utils/ListMarkerDrawer.h +15 -0
  320. package/ios/utils/ListMarkerDrawer.m +239 -0
  321. package/ios/utils/MentionBackground.h +26 -0
  322. package/ios/utils/MentionBackground.m +86 -0
  323. package/ios/utils/ParagraphStyleUtils.h +87 -0
  324. package/ios/utils/ParagraphStyleUtils.m +484 -0
  325. package/ios/utils/PasteboardUtils.h +48 -0
  326. package/ios/utils/PasteboardUtils.m +166 -0
  327. package/ios/utils/RTFExportUtils.h +24 -0
  328. package/ios/utils/RTFExportUtils.m +299 -0
  329. package/ios/utils/RuntimeKeys.h +44 -0
  330. package/ios/utils/RuntimeKeys.m +13 -0
  331. package/ios/utils/SelectionColorUtils.h +14 -0
  332. package/ios/utils/StylePropsUtils.h +1340 -0
  333. package/ios/utils/TaskListTapUtils.h +6 -0
  334. package/ios/utils/TaskListTapUtils.m +14 -0
  335. package/ios/utils/TextViewLayoutManager.h +14 -0
  336. package/ios/utils/TextViewLayoutManager.mm +198 -0
  337. package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -0
  338. package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -0
  339. package/ios/utils/TiptapExtractor.h +17 -0
  340. package/ios/utils/TiptapExtractor.m +367 -0
  341. package/ios/views/ENRMTiptapTextView.h +25 -0
  342. package/ios/views/ENRMTiptapTextView.m +247 -0
  343. package/package.json +70 -6
  344. package/react-native.config.js +10 -0
  345. package/src/EnrichedTiptapTextNativeComponent.ts +739 -0
  346. package/src/RichText.tsx +164 -0
  347. package/src/admonitionDefaults.ts +58 -0
  348. package/src/darkModeTextStyle.ts +99 -0
  349. package/src/headingDefaults.ts +21 -0
  350. package/src/index.tsx +4 -0
  351. package/src/linkVariantUtils.ts +120 -0
  352. package/src/normalizeTiptapStyle.ts +397 -0
  353. package/src/rn-globals.d.ts +1 -0
  354. package/src/styleUtils.ts +107 -0
  355. package/src/types/TiptapStyle.ts +532 -0
  356. package/src/types/TiptapStyleInternal.ts +299 -0
  357. package/src/types/linkPill.ts +9 -0
  358. package/src/utils/isDev.ts +4 -0
@@ -0,0 +1,673 @@
1
+ package com.renderrichtext
2
+
3
+ import android.content.Context
4
+ import android.content.res.Configuration
5
+ import android.graphics.Canvas
6
+ import android.os.Build
7
+ import android.os.Handler
8
+ import android.os.Looper
9
+ import android.text.Layout
10
+ import android.util.AttributeSet
11
+ import android.util.Log
12
+ import android.util.TypedValue
13
+ import android.view.MotionEvent
14
+ import android.view.View
15
+ import android.view.View.MeasureSpec
16
+ import com.facebook.react.bridge.ReadableMap
17
+ import com.facebook.react.uimanager.BackgroundStyleApplicator
18
+ import com.facebook.react.uimanager.PixelUtil
19
+ import com.facebook.react.uimanager.StateWrapper
20
+ import com.facebook.react.uimanager.style.LogicalEdge
21
+ import com.renderrichtext.accessibility.AccessibilityLabels
22
+ import com.renderrichtext.accessibility.AccessibleTiptapTextView
23
+ import com.renderrichtext.math.LatexErrorReporter
24
+ import com.renderrichtext.parser.Md4cFlags
25
+ import com.renderrichtext.parser.Parser
26
+ import com.renderrichtext.renderer.Renderer
27
+ import com.renderrichtext.spoiler.SpoilerCapable
28
+ import com.renderrichtext.spoiler.SpoilerOverlay
29
+ import com.renderrichtext.spoiler.SpoilerOverlayDrawer
30
+ import com.renderrichtext.styles.LinkPillContent
31
+ import com.renderrichtext.styles.StyleConfig
32
+ import com.renderrichtext.utils.common.BreakStrategyUtils
33
+ import com.renderrichtext.utils.common.EllipsizeUtils
34
+ import com.renderrichtext.utils.common.LinkRegexConfig
35
+ import com.renderrichtext.utils.common.emitCodeBlockPress
36
+ import com.renderrichtext.utils.text.TailFadeInAnimator
37
+ import com.renderrichtext.utils.text.interaction.CheckboxTouchHelper
38
+ import com.renderrichtext.utils.text.view.CodeBlockPressHost
39
+ import com.renderrichtext.utils.text.view.ImagePressHost
40
+ import com.renderrichtext.utils.text.view.LinkLongPressMovementMethod
41
+ import com.renderrichtext.utils.text.view.SelectionMenuConfig
42
+ import com.renderrichtext.utils.text.view.applySelectableState
43
+ import com.renderrichtext.utils.text.view.applySelectionColors
44
+ import com.renderrichtext.utils.text.view.cancelJSTouchForCheckboxTap
45
+ import com.renderrichtext.utils.text.view.cancelJSTouchForLinkTap
46
+ import com.renderrichtext.utils.text.view.createSelectionActionModeCallback
47
+ import com.renderrichtext.utils.text.view.emitFactoidPressEvent
48
+ import com.renderrichtext.utils.text.view.emitImagePressEvent
49
+ import com.renderrichtext.utils.text.view.emitLinkLongPressEvent
50
+ import com.renderrichtext.utils.text.view.emitLinkPressEvent
51
+ import com.renderrichtext.utils.text.view.emitMentionLongPressEvent
52
+ import com.renderrichtext.utils.text.view.emitMentionPressEvent
53
+ import com.renderrichtext.utils.text.view.setupAsTiptapTextView
54
+ import java.util.concurrent.Executors
55
+ import kotlin.math.ceil
56
+ import kotlin.math.roundToInt
57
+
58
+ /**
59
+ * EnrichedTiptapText that handles Tiptap parsing and rendering on a background thread.
60
+ * View starts invisible and becomes visible after render completes to avoid layout shift.
61
+ */
62
+ class EnrichedTiptapText
63
+ @JvmOverloads
64
+ constructor(
65
+ context: Context,
66
+ attrs: AttributeSet? = null,
67
+ defStyleAttr: Int = 0,
68
+ ) : AccessibleTiptapTextView(context, attrs, defStyleAttr),
69
+ SpoilerCapable,
70
+ ImagePressHost,
71
+ CodeBlockPressHost {
72
+ private val parser = Parser.shared
73
+ private val renderer = Renderer()
74
+ private var onLinkPressCallback: ((String) -> Unit)? = null
75
+ private var onLinkLongPressCallback: ((String) -> Unit)? = null
76
+ private var onFactoidPressCallback: ((String) -> Unit)? = null
77
+ private var onMentionPressCallback: ((String, String) -> Unit)? = null
78
+ private var onMentionLongPressCallback: ((String, String) -> Unit)? = null
79
+ private var onLatexErrorCallback: LatexErrorReporter? = null
80
+
81
+ private val reportedLatexErrors = HashSet<String>()
82
+
83
+ private val latexErrorReporter =
84
+ LatexErrorReporter { source, message, displayMode ->
85
+ val key = (if (displayMode) "B " else "I ") + source
86
+ if (reportedLatexErrors.add(key)) {
87
+ onLatexErrorCallback?.report(source, message, displayMode)
88
+ }
89
+ }
90
+ private val checkboxTouchHelper = CheckboxTouchHelper(this)
91
+
92
+ private val mainHandler = Handler(Looper.getMainLooper())
93
+ private val executor = Executors.newSingleThreadExecutor()
94
+ private var currentRenderId = 0L
95
+
96
+ val layoutManager = EnrichedTiptapTextLayoutManager(this)
97
+
98
+ // used to force a Yoga re-measure when a block image resolves its box height
99
+ var stateWrapper: StateWrapper? = null
100
+
101
+ private var contextMenuItemTexts: List<String> = emptyList()
102
+ var onContextMenuItemPressCallback: ((itemText: String, selectedText: String, selectionStart: Int, selectionEnd: Int) -> Unit)? = null
103
+
104
+ var tiptapStyle: StyleConfig? = null
105
+ private set
106
+
107
+ var currentTiptap: String = ""
108
+ private set
109
+
110
+ var md4cFlags: Md4cFlags = Md4cFlags.DEFAULT
111
+ private set
112
+ private var linkRegex: LinkRegexConfig? = null
113
+ private var inlineCodeLinkRegex: LinkRegexConfig? = null
114
+ private var isGFM: Boolean = false
115
+
116
+ private var lastKnownFontScale: Float = context.resources.configuration.fontScale
117
+ private var tiptapStyleMap: ReadableMap? = null
118
+
119
+ private var allowFontScaling: Boolean = true
120
+ private var maxFontSizeMultiplier: Float = 0f
121
+ private var allowTrailingMargin: Boolean = false
122
+ private var imageRequestHeaders: Map<String, String> = emptyMap()
123
+ private var linkPillContent: Map<String, LinkPillContent> = emptyMap()
124
+
125
+ private var streamingAnimation: Boolean = false
126
+ private var previousTextLength: Int = 0
127
+ private var fadeAnimator: TailFadeInAnimator? = null
128
+ override var spoilerOverlayDrawer: SpoilerOverlayDrawer? = null
129
+ private set
130
+ var spoilerOverlay: SpoilerOverlay = SpoilerOverlay.PARTICLES
131
+
132
+ private var pendingStyledText: CharSequence? = null
133
+
134
+ private var mdNumberOfLines: Int = 0
135
+ private var mdEllipsizeMode: String = EllipsizeUtils.DEFAULT_MODE
136
+ private var mdSelectable: Boolean = true
137
+
138
+ private var selectionColor: Int? = null
139
+ private var selectionHandleColor: Int? = null
140
+ private var selectionMenuConfig = SelectionMenuConfig()
141
+ private var baseHorizontalPaddingLeft = 0
142
+ private var baseHorizontalPaddingRight = 0
143
+ private var applyingContentBoxInset = false
144
+
145
+ init {
146
+ setupAsTiptapTextView()
147
+ customSelectionActionModeCallback =
148
+ createSelectionActionModeCallback(
149
+ this,
150
+ getCustomItemTexts = { contextMenuItemTexts },
151
+ getSelectionMenuConfig = { selectionMenuConfig },
152
+ onCustomItemPress = { itemText, selectedText, start, end ->
153
+ onContextMenuItemPressCallback?.invoke(itemText, selectedText, start, end)
154
+ },
155
+ )
156
+ }
157
+
158
+ fun setTiptapContent(tiptap: String) {
159
+ if (currentTiptap == tiptap) return
160
+ currentTiptap = tiptap
161
+ scheduleRender()
162
+ }
163
+
164
+ fun setTiptapStyle(style: ReadableMap?) {
165
+ tiptapStyleMap = style
166
+ // Register font scaling settings when style is set (view should have ID by now)
167
+ updateMeasurementStoreFontScaling()
168
+ val newStyle = style?.let { StyleConfig(it, context, allowFontScaling, maxFontSizeMultiplier) }
169
+ newStyle?.imageRequestHeaders = imageRequestHeaders
170
+ newStyle?.linkPillContent = linkPillContent
171
+ if (tiptapStyle == newStyle) return
172
+ tiptapStyle = newStyle
173
+ updateJustificationMode(newStyle)
174
+ scheduleRender()
175
+ }
176
+
177
+ fun setImageRequestHeaders(headers: Map<String, String>) {
178
+ if (imageRequestHeaders == headers) return
179
+ imageRequestHeaders = headers
180
+ tiptapStyle?.imageRequestHeaders = headers
181
+ scheduleRenderIfNeeded()
182
+ }
183
+
184
+ fun setLinkPillContent(content: Map<String, LinkPillContent>) {
185
+ if (linkPillContent == content) return
186
+ linkPillContent = content
187
+ tiptapStyle?.linkPillContent = content
188
+ scheduleRenderIfNeeded()
189
+ }
190
+
191
+ override fun onConfigurationChanged(newConfig: Configuration) {
192
+ super.onConfigurationChanged(newConfig)
193
+
194
+ if (!allowFontScaling) {
195
+ return
196
+ }
197
+
198
+ val newFontScale = newConfig.fontScale
199
+ if (newFontScale != lastKnownFontScale) {
200
+ lastKnownFontScale = newFontScale
201
+ recreateStyleConfig()
202
+ scheduleRenderIfNeeded()
203
+ }
204
+ }
205
+
206
+ fun setLinkRegex(config: LinkRegexConfig?) {
207
+ if (linkRegex == config) return
208
+ linkRegex = config
209
+ scheduleRenderIfNeeded()
210
+ }
211
+
212
+ fun setInlineCodeLinkRegex(config: LinkRegexConfig?) {
213
+ if (inlineCodeLinkRegex == config) return
214
+ inlineCodeLinkRegex = config
215
+ scheduleRenderIfNeeded()
216
+ }
217
+
218
+ fun setMd4cFlags(flags: Md4cFlags) {
219
+ if (md4cFlags == flags) return
220
+ md4cFlags = flags
221
+ scheduleRenderIfNeeded()
222
+ }
223
+
224
+ fun setIsGFM(value: Boolean) {
225
+ if (isGFM == value) return
226
+ isGFM = value
227
+ scheduleRenderIfNeeded()
228
+ }
229
+
230
+ fun setAllowFontScaling(allow: Boolean) {
231
+ if (allowFontScaling == allow) return
232
+ allowFontScaling = allow
233
+ updateMeasurementStoreFontScaling()
234
+ recreateStyleConfig()
235
+ scheduleRenderIfNeeded()
236
+ }
237
+
238
+ fun setMaxFontSizeMultiplier(multiplier: Float) {
239
+ if (maxFontSizeMultiplier == multiplier) return
240
+ maxFontSizeMultiplier = multiplier
241
+ updateMeasurementStoreFontScaling()
242
+ recreateStyleConfig()
243
+ scheduleRenderIfNeeded()
244
+ }
245
+
246
+ fun setAllowTrailingMargin(allow: Boolean) {
247
+ if (allowTrailingMargin == allow) return
248
+ allowTrailingMargin = allow
249
+ scheduleRenderIfNeeded()
250
+ }
251
+
252
+ fun setStreamingAnimation(enabled: Boolean) {
253
+ if (streamingAnimation == enabled) return
254
+ streamingAnimation = enabled
255
+ if (enabled) {
256
+ previousTextLength = text?.length ?: 0
257
+ } else {
258
+ fadeAnimator?.cancelAll()
259
+ fadeAnimator = null
260
+ previousTextLength = 0
261
+ }
262
+ }
263
+
264
+ fun commitProps() {
265
+ updateMeasurementStoreFontScaling()
266
+ }
267
+
268
+ private fun updateMeasurementStoreFontScaling() {
269
+ MeasurementStore.updateFontScalingSettings(id, allowFontScaling, maxFontSizeMultiplier)
270
+ }
271
+
272
+ private fun scheduleRenderIfNeeded() {
273
+ if (currentTiptap.isNotEmpty()) {
274
+ scheduleRender()
275
+ }
276
+ }
277
+
278
+ private fun recreateStyleConfig() {
279
+ tiptapStyleMap?.let { styleMap ->
280
+ tiptapStyle =
281
+ StyleConfig(styleMap, context, allowFontScaling, maxFontSizeMultiplier).also {
282
+ it.imageRequestHeaders = imageRequestHeaders
283
+ it.linkPillContent = linkPillContent
284
+ }
285
+ updateJustificationMode(tiptapStyle)
286
+ }
287
+ }
288
+
289
+ private fun updateJustificationMode(style: StyleConfig?) {
290
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
291
+ justificationMode =
292
+ if (style?.needsJustify == true) {
293
+ Layout.JUSTIFICATION_MODE_INTER_WORD
294
+ } else {
295
+ Layout.JUSTIFICATION_MODE_NONE
296
+ }
297
+ }
298
+ }
299
+
300
+ private fun scheduleRender() {
301
+ val style = tiptapStyle ?: return
302
+ val tiptap = currentTiptap
303
+ if (tiptap.isEmpty()) return
304
+
305
+ val textLinkRegex = linkRegex
306
+ val codeLinkRegex = inlineCodeLinkRegex
307
+ val renderId = ++currentRenderId
308
+
309
+ executor.execute {
310
+ try {
311
+ val ast =
312
+ parser.parseTiptap(tiptap, md4cFlags, isGFM, textLinkRegex, codeLinkRegex) ?: run {
313
+ mainHandler.post { if (renderId == currentRenderId && isAttachedToWindow) text = "" }
314
+ return@execute
315
+ }
316
+
317
+ renderer.configure(style, context, latexErrorReporter)
318
+ val styledText =
319
+ renderer.renderDocument(
320
+ ast,
321
+ onLinkPressCallback,
322
+ onLinkLongPressCallback,
323
+ onFactoidPressCallback,
324
+ onMentionPressCallback,
325
+ onMentionLongPressCallback,
326
+ )
327
+
328
+ mainHandler.post {
329
+ if (renderId == currentRenderId) {
330
+ if (isAttachedToWindow) {
331
+ applyRenderedText(styledText)
332
+ } else {
333
+ pendingStyledText = styledText
334
+ }
335
+ }
336
+ }
337
+ } catch (e: Exception) {
338
+ Log.e(TAG, "Render failed: ${e.message}", e)
339
+ mainHandler.post { if (renderId == currentRenderId && isAttachedToWindow) text = "" }
340
+ }
341
+ }
342
+ }
343
+
344
+ private fun applyRenderedText(styledText: CharSequence) {
345
+ val tailStart = previousTextLength
346
+
347
+ tiptapStyle?.paragraphStyle?.fontSize?.let {
348
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, it)
349
+ }
350
+
351
+ text = styledText
352
+
353
+ applyInteractivity()
354
+
355
+ renderer.getCollectedImageSpans().forEach { span ->
356
+ span.registerTextView(this)
357
+ }
358
+
359
+ spoilerOverlayDrawer = SpoilerOverlayDrawer.setupIfNeeded(this, styledText, spoilerOverlayDrawer, spoilerOverlay)
360
+
361
+ layoutManager.invalidateLayout()
362
+ accessibilityHelper.invalidateAccessibilityItems()
363
+
364
+ if (streamingAnimation) {
365
+ if (fadeAnimator == null) {
366
+ fadeAnimator = TailFadeInAnimator(this)
367
+ }
368
+ fadeAnimator?.animate(tailStart, styledText.length)
369
+ previousTextLength = styledText.length
370
+ }
371
+
372
+ applySelectionColors(selectionColor, selectionHandleColor)
373
+ }
374
+
375
+ // Trailing bottom margin included in the shadow-node measurement — must be
376
+ // mirrored when re-storing the measurement from the display text.
377
+ fun trailingMarginBottomPx(): Float = if (allowTrailingMargin) renderer.getLastElementMarginBottom() else 0f
378
+
379
+ fun setContextMenuItems(items: List<String>) {
380
+ contextMenuItemTexts = items
381
+ }
382
+
383
+ fun setSelectionMenuConfig(config: SelectionMenuConfig) {
384
+ if (selectionMenuConfig == config) return
385
+ selectionMenuConfig = config
386
+ }
387
+
388
+ fun setAccessibilityLabels(labels: AccessibilityLabels) {
389
+ accessibilityHelper.labels = labels
390
+ }
391
+
392
+ fun setIsSelectable(selectable: Boolean) {
393
+ mdSelectable = selectable
394
+ applyInteractivity()
395
+ }
396
+
397
+ fun setSelectionColor(color: Int?) {
398
+ if (selectionColor == color) return
399
+ selectionColor = color
400
+ applySelectionColors(selectionColor, selectionHandleColor)
401
+ }
402
+
403
+ fun setSelectionHandleColor(color: Int?) {
404
+ if (selectionHandleColor == color) return
405
+ selectionHandleColor = color
406
+ applySelectionColors(selectionColor, selectionHandleColor)
407
+ }
408
+
409
+ fun setTextBreakStrategy(strategy: String) {
410
+ MeasurementStore.updateBreakStrategy(id, strategy)
411
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
412
+ breakStrategy = BreakStrategyUtils.resolveBreakStrategy(strategy)
413
+ }
414
+ MeasurementStore.invalidate(id)
415
+ scheduleRenderIfNeeded()
416
+ }
417
+
418
+ fun setTiptapNumberOfLines(value: Int) {
419
+ val normalized = value.coerceAtLeast(0)
420
+ if (mdNumberOfLines == normalized) return
421
+ mdNumberOfLines = normalized
422
+ MeasurementStore.updateNumberOfLines(id, normalized)
423
+ applyLineLimitToView()
424
+ MeasurementStore.invalidate(id)
425
+ scheduleRenderIfNeeded()
426
+ }
427
+
428
+ fun setTiptapEllipsizeMode(value: String) {
429
+ if (mdEllipsizeMode == value) return
430
+ mdEllipsizeMode = value
431
+ MeasurementStore.updateEllipsizeMode(id, value)
432
+ applyLineLimitToView()
433
+ MeasurementStore.invalidate(id)
434
+ scheduleRenderIfNeeded()
435
+ }
436
+
437
+ // Mirrors RN's ReactTextView.updateView: only ellipsize when a line limit is
438
+ // set; unlimited restores the default unbounded, non-ellipsized state.
439
+ private fun applyLineLimitToView() {
440
+ if (mdNumberOfLines > 0) {
441
+ maxLines = mdNumberOfLines
442
+ ellipsize = EllipsizeUtils.resolveTruncateAt(mdEllipsizeMode)
443
+ } else {
444
+ maxLines = Integer.MAX_VALUE
445
+ ellipsize = null
446
+ }
447
+ applyInteractivity()
448
+ }
449
+
450
+ // The numberOfLines ellipsis and text selection / link taps are mutually
451
+ // exclusive on Android and cannot be reconciled. Both setTextIsSelectable(true)
452
+ // and a non-null movementMethod force the display text to a Spannable, which
453
+ // makes TextView pick DynamicLayout (TextView.useDynamicLayout). DynamicLayout
454
+ // has no setMaxLines, so the clamp is never baked into the layout and no
455
+ // ellipsis glyph is drawn (verified against AOSP TextView/DynamicLayout on
456
+ // API 35/36). To keep the ellipsis, a clamped view must be non-selectable AND
457
+ // drop its movement method; both are restored to the user's preference once
458
+ // the clamp is removed.
459
+ private fun applyInteractivity() {
460
+ if (mdNumberOfLines > 0) {
461
+ if (isTextSelectable) setTextIsSelectable(false)
462
+ movementMethod = null
463
+ } else {
464
+ applySelectableState(mdSelectable)
465
+ if (movementMethod !is LinkLongPressMovementMethod) {
466
+ movementMethod = LinkLongPressMovementMethod.createInstance()
467
+ }
468
+ }
469
+ }
470
+
471
+ fun emitOnLinkPress(url: String) {
472
+ emitLinkPressEvent(url)
473
+ }
474
+
475
+ fun emitOnFactoidPress(factoidId: String) {
476
+ emitFactoidPressEvent(factoidId)
477
+ }
478
+
479
+ fun emitOnMentionPress(
480
+ mentionId: String,
481
+ label: String,
482
+ ) {
483
+ emitMentionPressEvent(mentionId, label)
484
+ }
485
+
486
+ fun emitOnMentionLongPress(
487
+ mentionId: String,
488
+ label: String,
489
+ ) {
490
+ emitMentionLongPressEvent(mentionId, label)
491
+ }
492
+
493
+ fun emitOnLinkLongPress(url: String) {
494
+ emitLinkLongPressEvent(url)
495
+ }
496
+
497
+ override var imagePressEnabled: Boolean = false
498
+ private set
499
+
500
+ override fun emitOnImagePress(
501
+ url: String,
502
+ altText: String,
503
+ ) {
504
+ emitImagePressEvent(url, altText)
505
+ }
506
+
507
+ fun setEnableImagePress(enabled: Boolean) {
508
+ imagePressEnabled = enabled
509
+ }
510
+
511
+ override var codeBlockPressEnabled: Boolean = false
512
+ private set
513
+
514
+ override fun emitOnCodeBlockPress(
515
+ code: String,
516
+ language: String,
517
+ ) {
518
+ emitCodeBlockPress(this, code, language)
519
+ }
520
+
521
+ fun setEnableCodeBlockPress(enabled: Boolean) {
522
+ codeBlockPressEnabled = enabled
523
+ }
524
+
525
+ fun setOnLinkPressCallback(callback: (String) -> Unit) {
526
+ onLinkPressCallback = callback
527
+ }
528
+
529
+ fun setOnFactoidPressCallback(callback: (String) -> Unit) {
530
+ onFactoidPressCallback = callback
531
+ }
532
+
533
+ fun setOnMentionPressCallback(callback: (String, String) -> Unit) {
534
+ onMentionPressCallback = callback
535
+ }
536
+
537
+ fun setOnMentionLongPressCallback(callback: (String, String) -> Unit) {
538
+ onMentionLongPressCallback = callback
539
+ }
540
+
541
+ fun setOnLinkLongPressCallback(callback: (String) -> Unit) {
542
+ onLinkLongPressCallback = callback
543
+ }
544
+
545
+ fun setOnLatexErrorCallback(callback: LatexErrorReporter) {
546
+ onLatexErrorCallback = callback
547
+ }
548
+
549
+ fun setOnTaskListItemPressCallback(callback: ((taskIndex: Int, checked: Boolean, itemText: String) -> Unit)?) {
550
+ checkboxTouchHelper.onCheckboxTap = callback
551
+ }
552
+
553
+ fun setEnableTaskListItemToggle(enabled: Boolean) {
554
+ checkboxTouchHelper.isEnabled = enabled
555
+ }
556
+
557
+ fun cleanup() {
558
+ currentRenderId++
559
+ executor.shutdownNow()
560
+ mainHandler.removeCallbacksAndMessages(null)
561
+ pendingStyledText = null
562
+ fadeAnimator?.cancelAll()
563
+ fadeAnimator = null
564
+ }
565
+
566
+ override fun setPadding(
567
+ left: Int,
568
+ top: Int,
569
+ right: Int,
570
+ bottom: Int,
571
+ ) {
572
+ if (!applyingContentBoxInset) {
573
+ baseHorizontalPaddingLeft = left
574
+ baseHorizontalPaddingRight = right
575
+ }
576
+ super.setPadding(left, top, right, bottom)
577
+ }
578
+
579
+ override fun onMeasure(
580
+ widthMeasureSpec: Int,
581
+ heightMeasureSpec: Int,
582
+ ) {
583
+ applyContentBoxInset(widthMeasureSpec)
584
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
585
+ }
586
+
587
+ /**
588
+ * Fabric measures text at the content box (frame - padding - border) but the TextView's
589
+ * padding carries no border, so it wraps ~1dp wider and reserves a phantom trailing line
590
+ * under an unclamped box (#805). Fold the border into padding to match; restore the base
591
+ * padding when the cache or an exact frame is missing so no stale inset lingers.
592
+ */
593
+ private fun applyContentBoxInset(widthMeasureSpec: Int) {
594
+ val contentBox =
595
+ if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
596
+ MeasurementStore.contentBoxWidthPx(id)?.let { ceil(it).toInt() }?.takeIf { it > 0 }
597
+ } else {
598
+ null
599
+ }
600
+ val frame = MeasureSpec.getSize(widthMeasureSpec)
601
+ if (contentBox == null || frame <= 0) {
602
+ setHorizontalPadding(baseHorizontalPaddingLeft, baseHorizontalPaddingRight)
603
+ return
604
+ }
605
+
606
+ val totalInset = (frame - contentBox).coerceIn(0, frame)
607
+ val left = (baseHorizontalPaddingLeft + resolvedBorderPx(leftEdge = true)).coerceIn(0, totalInset)
608
+ val right = (totalInset - left).coerceAtLeast(0)
609
+ setHorizontalPadding(left, right)
610
+ }
611
+
612
+ // Physical border width (px); only the left/right split reads it, never the total inset.
613
+ private fun resolvedBorderPx(leftEdge: Boolean): Int {
614
+ val rtl = layoutDirection == View.LAYOUT_DIRECTION_RTL
615
+ val physical = if (leftEdge) LogicalEdge.LEFT else LogicalEdge.RIGHT
616
+ val logical = if (leftEdge != rtl) LogicalEdge.START else LogicalEdge.END
617
+ val dp =
618
+ BackgroundStyleApplicator.getBorderWidth(this, physical)
619
+ ?: BackgroundStyleApplicator.getBorderWidth(this, logical)
620
+ ?: BackgroundStyleApplicator.getBorderWidth(this, LogicalEdge.ALL)
621
+ ?: return 0
622
+ return PixelUtil.toPixelFromDIP(dp).roundToInt().coerceAtLeast(0)
623
+ }
624
+
625
+ private fun setHorizontalPadding(
626
+ left: Int,
627
+ right: Int,
628
+ ) {
629
+ if (left == paddingLeft && right == paddingRight) return
630
+ applyingContentBoxInset = true
631
+ super.setPadding(left, paddingTop, right, paddingBottom)
632
+ applyingContentBoxInset = false
633
+ }
634
+
635
+ override fun onAttachedToWindow() {
636
+ super.onAttachedToWindow()
637
+ pendingStyledText?.let {
638
+ pendingStyledText = null
639
+ applyRenderedText(it)
640
+ }
641
+ }
642
+
643
+ override fun onDetachedFromWindow() {
644
+ stopSpoilerAnimations()
645
+ super.onDetachedFromWindow()
646
+ }
647
+
648
+ override fun onDraw(canvas: Canvas) {
649
+ super.onDraw(canvas)
650
+ spoilerOverlayDrawer?.draw(canvas)
651
+ }
652
+
653
+ private fun stopSpoilerAnimations() {
654
+ spoilerOverlayDrawer?.stop()
655
+ spoilerOverlayDrawer = null
656
+ }
657
+
658
+ override fun onTouchEvent(event: MotionEvent): Boolean {
659
+ if (checkboxTouchHelper.onTouchEvent(event)) {
660
+ if (event.action == MotionEvent.ACTION_DOWN) {
661
+ cancelJSTouchForCheckboxTap(event)
662
+ }
663
+ return true
664
+ }
665
+ val result = super.onTouchEvent(event)
666
+ if (event.action == MotionEvent.ACTION_DOWN) cancelJSTouchForLinkTap(event)
667
+ return result
668
+ }
669
+
670
+ companion object {
671
+ private const val TAG = "EnrichedTiptapMeasure"
672
+ }
673
+ }
@@ -0,0 +1,24 @@
1
+ package com.renderrichtext
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+
5
+ class EnrichedTiptapTextLayoutManager(
6
+ private val view: EnrichedTiptapText,
7
+ ) {
8
+ private var forceHeightRecalculationCounter = 0
9
+
10
+ fun invalidateLayout() {
11
+ val text = view.text
12
+ val paint = view.paint
13
+ val heightChanged = MeasurementStore.store(view.id, text, paint, view.trailingMarginBottomPx())
14
+ if (!heightChanged) return
15
+ val stateWrapper = view.stateWrapper ?: return
16
+ val state = Arguments.createMap()
17
+ state.putInt("forceHeightRecalculationCounter", ++forceHeightRecalculationCounter)
18
+ stateWrapper.updateState(state)
19
+ }
20
+
21
+ fun releaseMeasurementStore() {
22
+ MeasurementStore.release(view.id)
23
+ }
24
+ }