@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.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 (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,286 @@
1
+ package com.renderrichtext.utils.text.interaction
2
+
3
+ import android.text.Spannable
4
+ import android.text.SpannableStringBuilder
5
+ import android.text.Spanned
6
+ import android.text.style.ForegroundColorSpan
7
+ import android.text.style.StrikethroughSpan
8
+ import android.widget.TextView
9
+ import com.renderrichtext.renderer.SpanStyleCache
10
+ import com.renderrichtext.spans.BaseListSpan
11
+ import com.renderrichtext.spans.CodeBlockSpan
12
+ import com.renderrichtext.spans.ListMarkerAnchorSpan
13
+ import com.renderrichtext.spans.TaskListSpan
14
+ import com.renderrichtext.styles.StyleConfig
15
+ import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
16
+
17
+ data class TaskListHitTestResult(
18
+ val taskIndex: Int,
19
+ val checked: Boolean,
20
+ val itemText: String,
21
+ )
22
+
23
+ object TaskListToggleUtils {
24
+ private val TASK_PATTERN = Regex("""^([ \t]*[-*+][ \t]+)\[[ xX]]""", RegexOption.MULTILINE)
25
+
26
+ fun toggleAtIndex(
27
+ tiptap: String,
28
+ index: Int,
29
+ checked: Boolean,
30
+ ): String {
31
+ val matches = TASK_PATTERN.findAll(tiptap).toList()
32
+ if (index < 0 || index >= matches.size) return tiptap
33
+
34
+ val match = matches[index]
35
+ val prefix = match.groupValues[1]
36
+
37
+ val replacement = "$prefix[${if (checked) "x" else " "}]"
38
+
39
+ return tiptap.replaceRange(match.range, replacement)
40
+ }
41
+ }
42
+
43
+ object TaskListTapUtils {
44
+ fun hitTest(
45
+ textView: TextView,
46
+ rawX: Float,
47
+ rawY: Float,
48
+ ): TaskListHitTestResult? =
49
+ with(textView) {
50
+ val layout = layout ?: return null
51
+ val spannable = text as? Spanned ?: return null
52
+
53
+ val x = rawX.toInt() - totalPaddingLeft + scrollX
54
+ val y = rawY.toInt() - totalPaddingTop + scrollY
55
+
56
+ val line = layout.getLineForVertical(y)
57
+
58
+ val taskSpan =
59
+ spannable
60
+ .getSpans(
61
+ layout.getLineStart(line),
62
+ layout.getLineEnd(line),
63
+ TaskListSpan::class.java,
64
+ ).maxByOrNull { it.depth } ?: return null
65
+
66
+ val isRtl = layout.getParagraphDirection(line) == android.text.Layout.DIR_RIGHT_TO_LEFT
67
+ if (isRtl) {
68
+ val lineRight = layout.getLineRight(line).toInt()
69
+ val indentWidth = lineRight - layout.getParagraphRight(line).toInt()
70
+ if (x <= layout.width - indentWidth) return null
71
+ } else {
72
+ val lineLeft = layout.getLineLeft(line).toInt()
73
+ val indentWidth = layout.getParagraphLeft(line).toInt() - lineLeft
74
+ if (x >= indentWidth) return null
75
+ }
76
+
77
+ val spanStart = spannable.getSpanStart(taskSpan)
78
+ val spanEnd = spannable.getSpanEnd(taskSpan)
79
+
80
+ val itemText =
81
+ spannable
82
+ .subSequence(spanStart, spanEnd)
83
+ .toString()
84
+ .substringBefore('\n')
85
+ .trim()
86
+
87
+ return TaskListHitTestResult(
88
+ taskIndex = taskSpan.taskIndex,
89
+ checked = taskSpan.isChecked,
90
+ itemText = itemText,
91
+ )
92
+ }
93
+
94
+ fun updateTaskListItemCheckedState(
95
+ textView: TextView,
96
+ targetIndex: Int,
97
+ newChecked: Boolean,
98
+ styleConfig: StyleConfig,
99
+ ): Boolean {
100
+ val text = textView.text
101
+ if (text !is Spannable) {
102
+ return false
103
+ }
104
+
105
+ val spannable = SpannableStringBuilder(text)
106
+ val targetSpans =
107
+ spannable
108
+ .getSpans(0, spannable.length, TaskListSpan::class.java)
109
+ .filter { it.taskIndex == targetIndex }
110
+ if (targetSpans.isEmpty()) {
111
+ return false
112
+ }
113
+
114
+ if (targetSpans.all { it.isChecked == newChecked }) {
115
+ return true
116
+ }
117
+
118
+ val itemDepth = targetSpans.first().depth
119
+ val spanStart = targetSpans.minOf { spannable.getSpanStart(it) }
120
+ val spanEnd = targetSpans.maxOf { spannable.getSpanEnd(it) }
121
+
122
+ val styleCache = SpanStyleCache(styleConfig)
123
+
124
+ fun toggledCopy(old: TaskListSpan): TaskListSpan =
125
+ TaskListSpan(
126
+ taskStyle = styleConfig.taskListStyle,
127
+ listStyle = styleConfig.listStyle,
128
+ depth = old.depth,
129
+ context = textView.context,
130
+ styleCache = styleCache,
131
+ taskIndex = targetIndex,
132
+ isChecked = newChecked,
133
+ drawsMarker = old.drawsMarker,
134
+ ancestorMarkerOffset = old.ancestorMarkerOffset,
135
+ markerExtraWidth = old.markerExtraWidth,
136
+ parentMarkerWidth = old.parentMarkerWidth,
137
+ )
138
+
139
+ // Layout draws leading margins in insertion order, and a re-added span moves to the end of it.
140
+ // Re-add the task's spans and everything attached after them (its ancestors) in their original
141
+ // order; the task's own nested items were attached first and stay where they are.
142
+ data class ListSpanState(
143
+ val span: BaseListSpan,
144
+ val start: Int,
145
+ val end: Int,
146
+ val flags: Int,
147
+ )
148
+
149
+ val coveringSpans = spannable.getSpans(spanStart, spanEnd, BaseListSpan::class.java)
150
+ val firstTarget = coveringSpans.indexOfFirst { it is TaskListSpan && it.taskIndex == targetIndex }
151
+ val listSpans =
152
+ coveringSpans.drop(firstTarget).map {
153
+ ListSpanState(it, spannable.getSpanStart(it), spannable.getSpanEnd(it), spannable.getSpanFlags(it))
154
+ }
155
+ listSpans.forEach { spannable.removeSpan(it.span) }
156
+ for ((old, start, end, flags) in listSpans) {
157
+ val replacement = if (old is TaskListSpan && old.taskIndex == targetIndex) toggledCopy(old) else old
158
+ spannable.setSpan(replacement, start, end, flags)
159
+ }
160
+
161
+ // The marker may be drawn by an anchor span wrapping an unattached copy
162
+ spannable
163
+ .getSpans(0, spannable.length, ListMarkerAnchorSpan::class.java)
164
+ .filter { (it.marker as? TaskListSpan)?.taskIndex == targetIndex }
165
+ .forEach { old ->
166
+ val start = spannable.getSpanStart(old)
167
+ val end = spannable.getSpanEnd(old)
168
+ spannable.removeSpan(old)
169
+ spannable.setSpan(
170
+ ListMarkerAnchorSpan(toggledCopy(old.marker as TaskListSpan)),
171
+ start,
172
+ end,
173
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
174
+ )
175
+ }
176
+
177
+ val taskStyle = styleConfig.taskListStyle
178
+ val checkedTextColor = taskStyle.checkedTextColor
179
+ val strikethrough = taskStyle.checkedStrikethrough
180
+
181
+ val excludedRanges =
182
+ (
183
+ spannable.getSpans(spanStart, spanEnd, BaseListSpan::class.java).filter { it.depth > itemDepth } +
184
+ spannable.getSpans(spanStart, spanEnd, CodeBlockSpan::class.java).toList()
185
+ ).map { spannable.getSpanStart(it) to spannable.getSpanEnd(it) }
186
+ .sortedBy { it.first }
187
+
188
+ applyDecorationsToRanges(
189
+ spannable = spannable,
190
+ spanStart = spanStart,
191
+ spanEnd = spanEnd,
192
+ excludedRanges = excludedRanges,
193
+ isChecked = newChecked,
194
+ checkedTextColor = checkedTextColor,
195
+ strikethrough = strikethrough,
196
+ styleConfig = styleConfig,
197
+ )
198
+
199
+ val imageSpans = text.getSpans(0, text.length, com.renderrichtext.spans.ImageSpan::class.java).toList()
200
+ val originalSpanStarts = imageSpans.associateWith { text.getSpanStart(it) }
201
+
202
+ textView.text = spannable
203
+
204
+ val newImageSpans = spannable.getSpans(0, spannable.length, com.renderrichtext.spans.ImageSpan::class.java)
205
+ imageSpans.forEach { originalSpan ->
206
+ val matchingSpan =
207
+ newImageSpans.firstOrNull {
208
+ it.imageUrl == originalSpan.imageUrl &&
209
+ spannable.getSpanStart(it) == originalSpanStarts[originalSpan]
210
+ }
211
+ (matchingSpan ?: originalSpan).registerTextView(textView)
212
+ }
213
+
214
+ textView.invalidate()
215
+
216
+ return true
217
+ }
218
+
219
+ private fun applyDecorationsToRanges(
220
+ spannable: Spannable,
221
+ spanStart: Int,
222
+ spanEnd: Int,
223
+ excludedRanges: List<Pair<Int, Int>>,
224
+ isChecked: Boolean,
225
+ checkedTextColor: Int,
226
+ strikethrough: Boolean,
227
+ styleConfig: StyleConfig,
228
+ ) {
229
+ var currentPos = spanStart
230
+ for ((start, end) in excludedRanges) {
231
+ if (start > currentPos) {
232
+ if (isChecked) {
233
+ applyCheckedSpans(spannable, currentPos, start, checkedTextColor, strikethrough)
234
+ } else {
235
+ removeCheckedSpans(spannable, currentPos, start, styleConfig)
236
+ }
237
+ }
238
+ currentPos = maxOf(currentPos, end)
239
+ }
240
+ if (currentPos < spanEnd) {
241
+ if (isChecked) {
242
+ applyCheckedSpans(spannable, currentPos, spanEnd, checkedTextColor, strikethrough)
243
+ } else {
244
+ removeCheckedSpans(spannable, currentPos, spanEnd, styleConfig)
245
+ }
246
+ }
247
+ }
248
+
249
+ private fun applyCheckedSpans(
250
+ spannable: Spannable,
251
+ start: Int,
252
+ end: Int,
253
+ color: Int,
254
+ strikethrough: Boolean,
255
+ ) {
256
+ if (color != 0) {
257
+ spannable.setSpan(ForegroundColorSpan(color), start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
258
+ }
259
+ if (strikethrough) {
260
+ spannable.setSpan(StrikethroughSpan(), start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
261
+ }
262
+ }
263
+
264
+ private fun removeCheckedSpans(
265
+ spannable: Spannable,
266
+ start: Int,
267
+ end: Int,
268
+ styleConfig: StyleConfig,
269
+ ) {
270
+ val strikethroughSpans = spannable.getSpans(start, end, StrikethroughSpan::class.java)
271
+ strikethroughSpans.forEach { spannable.removeSpan(it) }
272
+
273
+ val colorSpans = spannable.getSpans(start, end, ForegroundColorSpan::class.java)
274
+ colorSpans.forEach { spannable.removeSpan(it) }
275
+
276
+ val listStyleColor = styleConfig.listStyle.color
277
+ if (listStyleColor != 0) {
278
+ spannable.setSpan(
279
+ ForegroundColorSpan(listStyleColor),
280
+ start,
281
+ end,
282
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
283
+ )
284
+ }
285
+ }
286
+ }
@@ -0,0 +1,39 @@
1
+ package com.renderrichtext.utils.text.span
2
+
3
+ import android.graphics.Paint.FontMetricsInt
4
+ import kotlin.math.ceil
5
+ import kotlin.math.floor
6
+
7
+ /**
8
+ * Splits extra leading above and below the line, copied from RN
9
+ * CustomLineHeightSpan so we do not depend on React Native internal span types.
10
+ *
11
+ * Mirrors CustomLineHeightSpan.chooseHeight:
12
+ * https://github.com/react/react-native/blob/v0.86.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/CustomLineHeightSpan.kt#L44-L57
13
+ *
14
+ * Unlike RN, [lineHeight] is a floor, not a clamp: taller content grows the
15
+ * line instead of being clipped (for example, inline code taller than the paragraph).
16
+ */
17
+ internal fun applyLineHeight(
18
+ fm: FontMetricsInt,
19
+ lineHeight: Int,
20
+ start: Int,
21
+ end: Int,
22
+ textLength: Int,
23
+ ) {
24
+ val leading = lineHeight - ((-fm.ascent) + fm.descent)
25
+
26
+ // Use lineHeight as a floor: if the default line-height is greater, don't
27
+ // override it
28
+ if (leading > 0) {
29
+ fm.ascent -= ceil(leading / 2.0f).toInt()
30
+ fm.descent += floor(leading / 2.0f).toInt()
31
+ }
32
+
33
+ if (start == 0) {
34
+ fm.top = fm.ascent
35
+ }
36
+ if (end == textLength) {
37
+ fm.bottom = fm.descent
38
+ }
39
+ }
@@ -0,0 +1,142 @@
1
+ package com.renderrichtext.utils.text.span
2
+
3
+ import android.text.SpannableStringBuilder
4
+ import com.renderrichtext.spans.CodeBlockSpan
5
+ import com.renderrichtext.spans.ImageSpan
6
+ import com.renderrichtext.spans.LineHeightSpan
7
+ import com.renderrichtext.spans.LinkPillSpan
8
+ import com.renderrichtext.spans.MarginBottomSpan
9
+ import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
10
+ import android.text.style.LineHeightSpan as AndroidLineHeightSpan
11
+
12
+ fun createLineHeightSpan(lineHeight: Float): AndroidLineHeightSpan = LineHeightSpan(lineHeight)
13
+
14
+ /** The line height floor that link pills ask of their paragraph. */
15
+ private class LinkPillLineHeightSpan(
16
+ height: Float,
17
+ ) : AndroidLineHeightSpan by LineHeightSpan(height)
18
+
19
+ /**
20
+ * Raises the line height of every paragraph in [start]..[end] to the largest
21
+ * `pill.lineHeight` among its link pills. A line grows to fit a pill on its own, but only
22
+ * that line and only to the pill's box, so pills on consecutive lines touch. A style that
23
+ * sets `pill.lineHeight` gets a floor for the whole paragraph instead, which keeps its
24
+ * lines even and the pills apart.
25
+ *
26
+ * Nested blocks set line height over ranges that overlap, so a paragraph that already
27
+ * has its floor is left alone. [spanFlags] are those of the block's own line height span:
28
+ * line height spans apply in order, and the floor has to come before the spans that add a
29
+ * margin to a line, or it would swallow that margin.
30
+ */
31
+ fun applyLinkPillLineHeight(
32
+ builder: SpannableStringBuilder,
33
+ start: Int,
34
+ end: Int,
35
+ spanFlags: Int = SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
36
+ ) {
37
+ val heights = HashMap<Int, Float>()
38
+ for (pill in builder.getSpans(start, end, LinkPillSpan::class.java)) {
39
+ val pillStart = builder.getSpanStart(pill)
40
+ // getSpans also returns a pill that only touches the range.
41
+ if (pill.lineHeight <= 0 || pillStart < start || pillStart >= end) continue
42
+ val paragraphStart = builder.lastIndexOf('\n', pillStart - 1).let { if (it < start) start else it + 1 }
43
+ heights[paragraphStart] = maxOf(heights[paragraphStart] ?: 0f, pill.lineHeight)
44
+ }
45
+ for ((paragraphStart, height) in heights) {
46
+ val paragraphEnd = builder.indexOf('\n', paragraphStart).let { if (it < 0 || it >= end) end else it + 1 }
47
+ // getSpans also returns the floor of a neighbouring paragraph, which only touches this one.
48
+ val hasFloor =
49
+ builder
50
+ .getSpans(paragraphStart, paragraphEnd, LinkPillLineHeightSpan::class.java)
51
+ .any { builder.getSpanStart(it) <= paragraphStart && builder.getSpanEnd(it) > paragraphStart }
52
+ if (hasFloor) continue
53
+ builder.setSpan(LinkPillLineHeightSpan(height), paragraphStart, paragraphEnd, spanFlags)
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Applies [LineHeightSpan] to [start]..[end] but skips ranges occupied by
59
+ * block [ImageSpan]s (and their trailing '\n') and by [CodeBlockSpan]s —
60
+ * both of these apply their own font-metric adjustments that a fixed
61
+ * line height would override.
62
+ */
63
+ fun applyLineHeightSkippingImages(
64
+ builder: SpannableStringBuilder,
65
+ start: Int,
66
+ end: Int,
67
+ lineHeight: Float,
68
+ spanFlags: Int = SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
69
+ ) {
70
+ val blockImageRanges =
71
+ builder
72
+ .getSpans(start, end, ImageSpan::class.java)
73
+ .filter { !it.isInline }
74
+ .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
75
+
76
+ val codeBlockRanges =
77
+ builder
78
+ .getSpans(start, end, CodeBlockSpan::class.java)
79
+ .map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
80
+
81
+ val excludedRanges = (blockImageRanges + codeBlockRanges).sortedBy { it.first }
82
+
83
+ var pos = start
84
+ for ((exStart, exEnd) in excludedRanges) {
85
+ if (pos < exStart) {
86
+ builder.setSpan(
87
+ createLineHeightSpan(lineHeight),
88
+ pos,
89
+ exStart,
90
+ spanFlags,
91
+ )
92
+ }
93
+ val skipEnd = if (exEnd < end && builder[exEnd] == '\n') exEnd + 1 else exEnd
94
+ pos = maxOf(pos, skipEnd)
95
+ }
96
+ if (pos < end) {
97
+ builder.setSpan(
98
+ createLineHeightSpan(lineHeight),
99
+ pos,
100
+ end,
101
+ spanFlags,
102
+ )
103
+ }
104
+ applyLinkPillLineHeight(builder, start, end, spanFlags)
105
+ }
106
+
107
+ fun applyMarginTop(
108
+ builder: SpannableStringBuilder,
109
+ insertionPoint: Int,
110
+ marginTop: Float,
111
+ ) {
112
+ if (marginTop <= 0) return
113
+
114
+ // Insert a newline character to act as a vertical spacer
115
+ builder.insert(insertionPoint, "\n")
116
+
117
+ // Apply MarginBottomSpan to the spacer character to create the gap before the content
118
+ builder.setSpan(
119
+ MarginBottomSpan(marginTop),
120
+ insertionPoint,
121
+ insertionPoint + 1,
122
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
123
+ )
124
+ }
125
+
126
+ fun applyMarginBottom(
127
+ builder: SpannableStringBuilder,
128
+ marginBottom: Float,
129
+ ) {
130
+ val spacerStart = builder.length
131
+ builder.append("\n")
132
+ // Always create a MarginBottomSpan, even when marginBottom = 0.
133
+ // This ensures removeTrailingMargin can correctly identify the LAST element's
134
+ // margin value. Without a span on the last element, it would pick up a previous
135
+ // element's span (e.g. blockquote with marginBottom: 16) and use that wrong value.
136
+ builder.setSpan(
137
+ MarginBottomSpan(marginBottom),
138
+ spacerStart,
139
+ builder.length,
140
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
141
+ )
142
+ }
@@ -0,0 +1,43 @@
1
+ package com.renderrichtext.utils.text.span
2
+
3
+ import android.text.SpannableString
4
+ import android.text.Spanned
5
+
6
+ const val SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE = SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
7
+
8
+ /**
9
+ * `SPAN_EXCLUSIVE_EXCLUSIVE` with the maximum span priority.
10
+ *
11
+ * Higher-priority spans are iterated — and therefore applied and drawn — first,
12
+ * so lower-priority spans over the same range override them. Use this for a
13
+ * block span set after its children that must not override the inline spans
14
+ * inside it (e.g. [HeadingSpan]), or for a container background that must sit
15
+ * under inline pill/chip backgrounds (e.g. [BlockquoteSpan]).
16
+ */
17
+ const val SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE_PRIORITY =
18
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE or Spanned.SPAN_PRIORITY
19
+
20
+ /**
21
+ * `SPAN_EXCLUSIVE_EXCLUSIVE` with the maximum span priority, for a block-wide
22
+ * LineHeightSpan (e.g. a blockquote's uniform line height) that must be iterated
23
+ * BEFORE the per-block [MarginBottomSpan]s it overlaps. LineHeightSpans are
24
+ * applied in iteration order and higher priority iterates first, so this span
25
+ * sets the base line height and the lower-priority margin span then adds its gap
26
+ * on top instead of being normalized away. Without it a line-height pass applied
27
+ * after the margins (e.g. in BlockquoteTextRenderer.postProcess) would erase them
28
+ * and clip the first line's ascent.
29
+ */
30
+ const val SPAN_FLAGS_LINE_HEIGHT_PRIORITY =
31
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE or Spanned.SPAN_PRIORITY
32
+
33
+ /**
34
+ * `SPAN_EXCLUSIVE_EXCLUSIVE` with a span priority just above the default.
35
+ *
36
+ * List item spans are set after the item's children, so at the default
37
+ * priority they apply after inline spans such as `CodeSpan` and replace their
38
+ * font and size. This priority applies them first, while staying below
39
+ * [SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE_PRIORITY] so a blockquote still draws its
40
+ * leading margin before a list's.
41
+ */
42
+ const val SPAN_FLAGS_LIST_ITEM =
43
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE or (1 shl Spanned.SPAN_PRIORITY_SHIFT)
@@ -0,0 +1,30 @@
1
+ package com.renderrichtext.utils.text.span
2
+
3
+ import android.text.Spanned
4
+ import com.renderrichtext.spans.ImageSpan
5
+ import com.renderrichtext.spans.LinkPillSpan
6
+
7
+ /**
8
+ * Primes every span whose size depends on the layout width (pills, images). Call it
9
+ * before building any layout over [text]. [includeImages] is off for a visible text
10
+ * view, whose image spans size themselves from the view they are registered with.
11
+ *
12
+ * Returns true when a pill's width limit changed, i.e. an existing layout is stale.
13
+ */
14
+ fun prepareWidthAwareSpans(
15
+ text: CharSequence?,
16
+ widthPx: Int,
17
+ includeImages: Boolean = true,
18
+ ): Boolean {
19
+ val spanned = text as? Spanned ?: return false
20
+ // widthPx == 1 is the coerceAtLeast(1) fallback for a not-yet-measured view. Priming
21
+ // with it would squeeze pills on screen to a sliver until the next real measure.
22
+ if (widthPx <= 1) return false
23
+ val pillsChanged = LinkPillSpan.prepareForMeasurement(spanned, widthPx)
24
+ if (includeImages) {
25
+ spanned
26
+ .getSpans(0, spanned.length, ImageSpan::class.java)
27
+ .forEach { it.prepareForMeasurement(spanned, widthPx) }
28
+ }
29
+ return pillsChanged
30
+ }
@@ -0,0 +1,15 @@
1
+ package com.renderrichtext.utils.text.view
2
+
3
+ /**
4
+ * Implemented by the commonmark host view so [LinkLongPressMovementMethod] can
5
+ * gate and dispatch taps landing on a code block span. GFM uses a separate
6
+ * CodeBlockContainerView, so its host walk resolves to none.
7
+ */
8
+ interface CodeBlockPressHost {
9
+ val codeBlockPressEnabled: Boolean
10
+
11
+ fun emitOnCodeBlockPress(
12
+ code: String,
13
+ language: String,
14
+ )
15
+ }
@@ -0,0 +1,19 @@
1
+ package com.renderrichtext.utils.text.view
2
+
3
+ /**
4
+ * Implemented by the React-tagged host views (EnrichedTiptapText and the GFM
5
+ * EnrichedTiptap container) so [LinkLongPressMovementMethod] can gate and
6
+ * dispatch image taps without knowing which flavor owns the tapped TextView.
7
+ *
8
+ * In GFM the tapped TextView is an internal segment child, so the movement
9
+ * method walks up to the nearest host - the same routing ImageSpan uses for its
10
+ * box-height notifications.
11
+ */
12
+ interface ImagePressHost {
13
+ val imagePressEnabled: Boolean
14
+
15
+ fun emitOnImagePress(
16
+ url: String,
17
+ altText: String,
18
+ )
19
+ }
@@ -0,0 +1,88 @@
1
+ package com.renderrichtext.utils.text.view
2
+
3
+ import android.view.MotionEvent
4
+ import android.view.View
5
+ import android.widget.TextView
6
+ import com.facebook.react.bridge.ReactContext
7
+ import com.facebook.react.uimanager.UIManagerHelper
8
+ import com.facebook.react.uimanager.events.NativeGestureUtil
9
+ import com.renderrichtext.events.FactoidPressEvent
10
+ import com.renderrichtext.events.ImagePressEvent
11
+ import com.renderrichtext.events.LinkLongPressEvent
12
+ import com.renderrichtext.events.LinkPressEvent
13
+ import com.renderrichtext.events.MentionLongPressEvent
14
+ import com.renderrichtext.events.MentionPressEvent
15
+
16
+ fun View.emitMentionPressEvent(
17
+ mentionId: String,
18
+ label: String,
19
+ ) {
20
+ val reactContext = context as? ReactContext ?: return
21
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
22
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
23
+ dispatcher?.dispatchEvent(MentionPressEvent(surfaceId, id, mentionId, label))
24
+ }
25
+
26
+ fun View.emitMentionLongPressEvent(
27
+ mentionId: String,
28
+ label: String,
29
+ ) {
30
+ val reactContext = context as? ReactContext ?: return
31
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
32
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
33
+ dispatcher?.dispatchEvent(MentionLongPressEvent(surfaceId, id, mentionId, label))
34
+ }
35
+
36
+ fun View.emitFactoidPressEvent(factoidId: String) {
37
+ val reactContext = context as? ReactContext ?: return
38
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
39
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
40
+ dispatcher?.dispatchEvent(FactoidPressEvent(surfaceId, id, factoidId))
41
+ }
42
+
43
+ fun View.emitLinkPressEvent(url: String) {
44
+ val reactContext = context as? ReactContext ?: return
45
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
46
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
47
+ dispatcher?.dispatchEvent(LinkPressEvent(surfaceId, id, url))
48
+ }
49
+
50
+ fun View.emitImagePressEvent(
51
+ url: String,
52
+ altText: String,
53
+ ) {
54
+ val reactContext = context as? ReactContext ?: return
55
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
56
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
57
+ dispatcher?.dispatchEvent(ImagePressEvent(surfaceId, id, url, altText))
58
+ }
59
+
60
+ fun View.emitLinkLongPressEvent(url: String) {
61
+ val reactContext = context as? ReactContext ?: return
62
+ val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
63
+ val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
64
+ dispatcher?.dispatchEvent(LinkLongPressEvent(surfaceId, id, url))
65
+ }
66
+
67
+ /**
68
+ * Cancels the JS touch for an active link tap, preventing parent
69
+ * Pressable/TouchableOpacity from firing onPress for the same tap.
70
+ *
71
+ * Parent intercept is handled by [LinkLongPressMovementMethod], which
72
+ * disallows it for the press and releases it when the press ends.
73
+ */
74
+ fun TextView.cancelJSTouchForLinkTap(event: MotionEvent) {
75
+ val currentMovementMethod = movementMethod
76
+ if (currentMovementMethod is LinkLongPressMovementMethod && currentMovementMethod.isLinkTouchActive) {
77
+ NativeGestureUtil.notifyNativeGestureStarted(this, event)
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Cancels the JS touch unconditionally, preventing parent
83
+ * Pressable/TouchableOpacity from firing onPress for the same gesture.
84
+ */
85
+ fun View.cancelJSTouchForCheckboxTap(event: MotionEvent) {
86
+ parent?.requestDisallowInterceptTouchEvent(true)
87
+ NativeGestureUtil.notifyNativeGestureStarted(this, event)
88
+ }