@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,133 @@
1
+ package com.renderrichtext.renderer
2
+
3
+ import android.text.SpannableStringBuilder
4
+ import com.renderrichtext.parser.TiptapASTNode
5
+ import com.renderrichtext.spans.ListItemSpacingSpan
6
+ import com.renderrichtext.spans.MarginBottomSpan
7
+ import com.renderrichtext.spans.OrderedListSpan
8
+ import com.renderrichtext.styles.ListStyle
9
+ import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
10
+ import com.renderrichtext.utils.text.span.applyLineHeightSkippingImages
11
+ import com.renderrichtext.utils.text.span.applyMarginTop
12
+
13
+ class ListRenderer(
14
+ private val config: RendererConfig,
15
+ private val isOrdered: Boolean,
16
+ ) : NodeRenderer {
17
+ override fun render(
18
+ node: TiptapASTNode,
19
+ builder: SpannableStringBuilder,
20
+ onLinkPress: ((String) -> Unit)?,
21
+ onLinkLongPress: ((String) -> Unit)?,
22
+ onFactoidPress: ((String) -> Unit)?,
23
+ factory: RendererFactory,
24
+ ) {
25
+ val start = builder.length
26
+ val listStyle = config.style.listStyle
27
+ val listType = if (isOrdered) BlockStyleContext.ListType.ORDERED else BlockStyleContext.ListType.UNORDERED
28
+
29
+ val startNumber =
30
+ if (isOrdered) {
31
+ node.getAttribute("start")?.toIntOrNull()?.coerceAtLeast(0) ?: 1
32
+ } else {
33
+ 1
34
+ }
35
+
36
+ val markerExtraWidth = if (isOrdered) markerExtraWidth(node, listStyle, startNumber, factory) else 0f
37
+ val contextManager = ListContextManager(factory.blockStyleContext)
38
+ val entryState = contextManager.enterList(listType, listStyle, startNumber, markerExtraWidth)
39
+
40
+ // For top-level lists, insert marginTop spacer before rendering content
41
+ if (entryState.previousDepth == 0) {
42
+ applyMarginTop(builder, start, listStyle.marginTop)
43
+ }
44
+
45
+ // Track start index after the potential 1-character marginTop spacer
46
+ val contentStart = if (entryState.previousDepth == 0 && listStyle.marginTop > 0) start + 1 else start
47
+
48
+ // Ensure nested lists start on a new line if the parent hasn't provided one
49
+ if (entryState.previousDepth > 0 && builder.isNotEmpty() && builder.last() != '\n') {
50
+ builder.append("\n")
51
+ }
52
+
53
+ try {
54
+ factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
55
+ } finally {
56
+ contextManager.exitList(entryState)
57
+ }
58
+
59
+ if (builder.length > contentStart) {
60
+ applyListSpacing(builder, contentStart, entryState.previousDepth, listStyle, factory.blockStyleContext)
61
+ }
62
+ }
63
+
64
+ /** Lists under 100 keep the `99.` column, so only numbers from 100 on widen it. Task items draw a checkbox. */
65
+ private fun markerExtraWidth(
66
+ node: TiptapASTNode,
67
+ listStyle: ListStyle,
68
+ startNumber: Int,
69
+ factory: RendererFactory,
70
+ ): Float {
71
+ val wideNumbers = mutableListOf<Int>()
72
+ var number = startNumber
73
+ for (item in node.children) {
74
+ if (item.type != TiptapASTNode.NodeType.ListItem) continue
75
+ if (number >= 100 && item.attributes["isTask"] != "true") wideNumbers += number
76
+ number++
77
+ }
78
+ return OrderedListSpan.measureExtraWidth(listStyle, factory.context, wideNumbers)
79
+ }
80
+
81
+ private fun applyListSpacing(
82
+ builder: SpannableStringBuilder,
83
+ start: Int,
84
+ depth: Int,
85
+ style: ListStyle,
86
+ styleContext: BlockStyleContext,
87
+ ) {
88
+ // TODO: LineHeightSpan may also clip superscript/subscript glyphs that extend
89
+ // outside the normal line bounds. A similar "skip" strategy as for images may
90
+ // be needed here once super/subscript usage in practice is better understood.
91
+ applyLineHeightSkippingImages(builder, start, builder.length, style.lineHeight)
92
+
93
+ // Item spacing and external bottom margin are only handled by the root-level list
94
+ if (depth == 0) {
95
+ applyItemSpacing(builder, start, style.itemSpacing, styleContext)
96
+
97
+ builder.append("\n")
98
+ builder.setSpan(
99
+ MarginBottomSpan(style.marginBottom),
100
+ builder.length - 1,
101
+ builder.length,
102
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
103
+ )
104
+ }
105
+ }
106
+
107
+ // Inserts the vertical gap between consecutive items (including nested ones)
108
+ // by spanning the newline that precedes each item start. The first item of
109
+ // the root list gets no spacing above it.
110
+ private fun applyItemSpacing(
111
+ builder: SpannableStringBuilder,
112
+ start: Int,
113
+ itemSpacing: Float,
114
+ styleContext: BlockStyleContext,
115
+ ) {
116
+ if (itemSpacing <= 0f) return
117
+
118
+ val itemStarts =
119
+ styleContext.listItemStarts
120
+ .filter { it in start until builder.length }
121
+ .distinct()
122
+ .sorted()
123
+
124
+ for (itemStart in itemStarts.drop(1)) {
125
+ builder.setSpan(
126
+ ListItemSpacingSpan(itemSpacing),
127
+ itemStart - 1,
128
+ itemStart,
129
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
130
+ )
131
+ }
132
+ }
133
+ }
@@ -0,0 +1,85 @@
1
+ package com.renderrichtext.renderer
2
+
3
+ import android.text.SpannableStringBuilder
4
+ import com.facebook.react.uimanager.PixelUtil
5
+ import com.renderrichtext.parser.TiptapASTNode
6
+ import com.renderrichtext.spans.MentionChipSpan
7
+ import com.renderrichtext.spans.MentionSpan
8
+ import com.renderrichtext.spans.TextSpan
9
+ import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
10
+
11
+ /**
12
+ * Tiptap mention atom. The node has no children; the visible text is `@` plus
13
+ * `label`, or `id` when `label` is blank. That is the suggestion character the
14
+ * Mention extension inserts.
15
+ *
16
+ * Weight is medium (`500`), the Android face used for a name inside body text.
17
+ * iOS uses semibold for the same role.
18
+ */
19
+ class MentionRenderer(
20
+ private val config: RendererConfig,
21
+ ) : NodeRenderer {
22
+ override fun render(
23
+ node: TiptapASTNode,
24
+ builder: SpannableStringBuilder,
25
+ onLinkPress: ((String) -> Unit)?,
26
+ onLinkLongPress: ((String) -> Unit)?,
27
+ onFactoidPress: ((String) -> Unit)?,
28
+ factory: RendererFactory,
29
+ ) {
30
+ val mentionId = node.getAttribute("id").orEmpty()
31
+ val label = node.getAttribute("label").orEmpty()
32
+ val name = label.ifBlank { mentionId }
33
+ val text = "@$name"
34
+ val mentionStyle = config.style.mentionStyle
35
+ val block = factory.blockStyleContext.currentBlockStyleOrNull()
36
+ val fontSize = block?.fontSize ?: config.style.paragraphStyle.fontSize
37
+ val fontFamily = block?.fontFamily ?: config.style.paragraphStyle.fontFamily
38
+
39
+ val start = builder.length
40
+ builder.append(text)
41
+ val end = builder.length
42
+ builder.setSpan(
43
+ TextSpan(
44
+ BlockStyle(
45
+ fontSize = fontSize,
46
+ fontFamily = fontFamily,
47
+ fontWeight = "500",
48
+ color = mentionStyle.color,
49
+ ),
50
+ factory.context,
51
+ ),
52
+ start,
53
+ end,
54
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
55
+ )
56
+ builder.setSpan(
57
+ MentionSpan(
58
+ mentionId,
59
+ label,
60
+ mentionStyle.color,
61
+ factory.onMentionPress,
62
+ factory.onMentionLongPress,
63
+ ),
64
+ start,
65
+ end,
66
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
67
+ )
68
+ builder.setSpan(
69
+ MentionChipSpan(
70
+ backgroundColor = mentionStyle.backgroundColor,
71
+ textColor = mentionStyle.color,
72
+ paddingX = PixelUtil.toPixelFromDIP(PADDING_X_DP),
73
+ paddingY = PixelUtil.toPixelFromDIP(PADDING_Y_DP),
74
+ ),
75
+ start,
76
+ end,
77
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
78
+ )
79
+ }
80
+
81
+ companion object {
82
+ private const val PADDING_X_DP = 3f
83
+ private const val PADDING_Y_DP = 1f
84
+ }
85
+ }
@@ -0,0 +1,207 @@
1
+ package com.renderrichtext.renderer
2
+
3
+ import android.content.Context
4
+ import android.text.SpannableStringBuilder
5
+ import android.text.Spanned
6
+ import android.text.style.MetricAffectingSpan
7
+ import com.renderrichtext.math.LatexErrorReporter
8
+ import com.renderrichtext.parser.TiptapASTNode
9
+ import com.renderrichtext.spans.ImageSpan
10
+ import com.renderrichtext.styles.StyleConfig
11
+ import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
12
+
13
+ interface NodeRenderer {
14
+ fun render(
15
+ node: TiptapASTNode,
16
+ builder: SpannableStringBuilder,
17
+ onLinkPress: ((String) -> Unit)?,
18
+ onLinkLongPress: ((String) -> Unit)?,
19
+ onFactoidPress: ((String) -> Unit)?,
20
+ factory: RendererFactory,
21
+ )
22
+ }
23
+
24
+ data class RendererConfig(
25
+ val style: StyleConfig,
26
+ val onLatexError: LatexErrorReporter? = null,
27
+ )
28
+
29
+ class RendererFactory(
30
+ private val config: RendererConfig,
31
+ val context: Context,
32
+ private val onImageSpanCreated: (ImageSpan) -> Unit,
33
+ ) {
34
+ val blockStyleContext = BlockStyleContext()
35
+
36
+ val styleCache = SpanStyleCache(config.style)
37
+
38
+ /** Set for the duration of one render. Mention spans capture the values. */
39
+ var onMentionPress: ((String, String) -> Unit)? = null
40
+ var onMentionLongPress: ((String, String) -> Unit)? = null
41
+
42
+ /**
43
+ * Spans registered here are applied after the full document tree is rendered, so they
44
+ * always end up after block-level spans in the SpannableStringBuilder's internal array.
45
+ * See [BaselineShiftRenderer] for the root cause and the proper long-term fix.
46
+ *
47
+ * The range is kept as offsets. A renderer that inserts text into the builder after
48
+ * its children rendered (the blockquote padding spacer) must report it through
49
+ * [shiftDeferredSpans], so the recorded ranges keep following their text.
50
+ */
51
+ private class DeferredSpan(
52
+ val span: MetricAffectingSpan,
53
+ var start: Int,
54
+ var end: Int,
55
+ )
56
+
57
+ private val deferredSpans = mutableListOf<DeferredSpan>()
58
+ private val afterRenderActions = mutableListOf<(Spanned) -> Unit>()
59
+
60
+ fun registerDeferredSpan(
61
+ span: MetricAffectingSpan,
62
+ start: Int,
63
+ end: Int,
64
+ ) {
65
+ deferredSpans.add(DeferredSpan(span, start, end))
66
+ }
67
+
68
+ /** Runs [action] on the finished text, once every block-level span is in place. */
69
+ fun runAfterRender(action: (Spanned) -> Unit) {
70
+ afterRenderActions.add(action)
71
+ }
72
+
73
+ /** Call right after inserting [length] characters at [position] into the builder being rendered. */
74
+ fun shiftDeferredSpans(
75
+ position: Int,
76
+ length: Int,
77
+ ) {
78
+ // Spans are registered in document order, so only the most recent ones can lie at
79
+ // or after the insertion point.
80
+ for (index in deferredSpans.indices.reversed()) {
81
+ val deferred = deferredSpans[index]
82
+ if (deferred.end <= position) break
83
+ if (deferred.start >= position) deferred.start += length
84
+ deferred.end += length
85
+ }
86
+ }
87
+
88
+ fun flushDeferredSpans(builder: SpannableStringBuilder) {
89
+ for (deferred in deferredSpans) {
90
+ builder.setSpan(deferred.span, deferred.start, deferred.end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
91
+ }
92
+ deferredSpans.clear()
93
+ afterRenderActions.forEach { it(builder) }
94
+ afterRenderActions.clear()
95
+ }
96
+
97
+ fun resetForNewRender() {
98
+ blockStyleContext.resetForNewRender()
99
+ deferredSpans.clear()
100
+ afterRenderActions.clear()
101
+ }
102
+
103
+ fun createImageSpan(
104
+ imageUrl: String,
105
+ isInline: Boolean,
106
+ altText: String,
107
+ ): ImageSpan =
108
+ ImageSpan(
109
+ context = context,
110
+ imageUrl = imageUrl,
111
+ styleConfig = config.style,
112
+ isInline = isInline,
113
+ altText = altText,
114
+ )
115
+
116
+ private val textRenderer = TextRenderer()
117
+ private val lineBreakRenderer = LineBreakRenderer()
118
+ private val softBreakRenderer = SoftBreakRenderer()
119
+
120
+ private val renderers: Map<TiptapASTNode.NodeType, NodeRenderer> by lazy {
121
+ buildMap {
122
+ put(TiptapASTNode.NodeType.Paragraph, ParagraphRenderer(config))
123
+ put(TiptapASTNode.NodeType.Heading, HeadingRenderer(config))
124
+ put(TiptapASTNode.NodeType.Blockquote, BlockquoteRenderer(config))
125
+ // A list-nested admonition falls back to the inline blockquote renderer
126
+ // (no themed header); top-level admonitions use the segment container path.
127
+ put(TiptapASTNode.NodeType.Admonition, BlockquoteRenderer(config))
128
+ put(TiptapASTNode.NodeType.CodeBlock, CodeBlockRenderer(config))
129
+ put(TiptapASTNode.NodeType.UnorderedList, ListRenderer(config, isOrdered = false))
130
+ put(TiptapASTNode.NodeType.OrderedList, ListRenderer(config, isOrdered = true))
131
+ put(TiptapASTNode.NodeType.ListItem, ListItemRenderer(config))
132
+ put(TiptapASTNode.NodeType.Text, textRenderer)
133
+ put(TiptapASTNode.NodeType.Link, LinkRenderer(config))
134
+ put(TiptapASTNode.NodeType.Strong, StrongRenderer(config))
135
+ put(TiptapASTNode.NodeType.Emphasis, EmphasisRenderer(config))
136
+ put(TiptapASTNode.NodeType.Strikethrough, StrikethroughRenderer(config))
137
+ put(TiptapASTNode.NodeType.Underline, UnderlineRenderer(config))
138
+ put(TiptapASTNode.NodeType.Highlight, HighlightRenderer())
139
+ put(TiptapASTNode.NodeType.Code, CodeRenderer(config))
140
+ put(TiptapASTNode.NodeType.Factoid, FactoidRenderer(config))
141
+ put(TiptapASTNode.NodeType.Mention, MentionRenderer(config))
142
+ put(TiptapASTNode.NodeType.LineBreak, lineBreakRenderer)
143
+ put(TiptapASTNode.NodeType.SoftBreak, softBreakRenderer)
144
+ put(TiptapASTNode.NodeType.ThematicBreak, ThematicBreakRenderer(config))
145
+ put(TiptapASTNode.NodeType.BlankLine, BlankLineRenderer(config))
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Called by ImageRenderer to report a new span to the collector.
151
+ */
152
+ fun registerImageSpan(span: ImageSpan) {
153
+ onImageSpanCreated(span)
154
+ }
155
+
156
+ fun getRenderer(node: TiptapASTNode): NodeRenderer =
157
+ renderers[node.type] ?: run {
158
+ android.util.Log.w("RendererFactory", "No renderer for: ${node.type}")
159
+ textRenderer
160
+ }
161
+
162
+ fun renderChildren(
163
+ node: TiptapASTNode,
164
+ builder: SpannableStringBuilder,
165
+ onLinkPress: ((String) -> Unit)?,
166
+ onLinkLongPress: ((String) -> Unit)?,
167
+ onFactoidPress: ((String) -> Unit)?,
168
+ ) {
169
+ renderNodes(node.children, builder, onLinkPress, onLinkLongPress, onFactoidPress)
170
+ }
171
+
172
+ /**
173
+ * Renders a flat list of sibling nodes in order, dispatching each to its
174
+ * NodeRenderer. Renders a block's own child content directly - e.g. a GFM
175
+ * blockquote's content once its nested block segments have been split out -
176
+ * without wrapping it in a synthetic root node.
177
+ */
178
+ fun renderNodes(
179
+ nodes: List<TiptapASTNode>,
180
+ builder: SpannableStringBuilder,
181
+ onLinkPress: ((String) -> Unit)?,
182
+ onLinkLongPress: ((String) -> Unit)?,
183
+ onFactoidPress: ((String) -> Unit)?,
184
+ ) {
185
+ nodes.forEach { node ->
186
+ getRenderer(node).render(node, builder, onLinkPress, onLinkLongPress, onFactoidPress, this)
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Improved helper for applying spans to blocks of text.
192
+ */
193
+ inline fun renderWithSpan(
194
+ builder: SpannableStringBuilder,
195
+ renderContent: () -> Unit,
196
+ applySpan: (start: Int, end: Int, blockStyle: BlockStyle) -> Unit,
197
+ ) {
198
+ val start = builder.length
199
+ renderContent()
200
+ val end = builder.length
201
+
202
+ if (end > start) {
203
+ val blockStyle = blockStyleContext.requireBlockStyle()
204
+ applySpan(start, end, blockStyle)
205
+ }
206
+ }
207
+ }
@@ -0,0 +1,91 @@
1
+ package com.renderrichtext.renderer
2
+
3
+ import android.text.SpannableStringBuilder
4
+ import android.text.style.AlignmentSpan
5
+ import com.renderrichtext.parser.TiptapASTNode
6
+ import com.renderrichtext.styles.ParagraphStyle
7
+ import com.renderrichtext.utils.text.extensions.containsBlockImage
8
+ import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
9
+ import com.renderrichtext.utils.text.span.applyLineHeightSkippingImages
10
+ import com.renderrichtext.utils.text.span.applyMarginBottom
11
+ import com.renderrichtext.utils.text.span.applyMarginTop
12
+
13
+ class ParagraphRenderer(
14
+ private val config: RendererConfig,
15
+ ) : NodeRenderer {
16
+ override fun render(
17
+ node: TiptapASTNode,
18
+ builder: SpannableStringBuilder,
19
+ onLinkPress: ((String) -> Unit)?,
20
+ onLinkLongPress: ((String) -> Unit)?,
21
+ onFactoidPress: ((String) -> Unit)?,
22
+ factory: RendererFactory,
23
+ ) {
24
+ val context = factory.blockStyleContext
25
+
26
+ // Inside a list, render tight with a single newline - lists own their item spacing.
27
+ if (context.listDepth > 0) {
28
+ factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
29
+ builder.append("\n")
30
+ return
31
+ }
32
+
33
+ val start = builder.length
34
+ val style = config.style.paragraphStyle
35
+
36
+ // Inside a blockquote, keep the quote's block style (font/color) and line height
37
+ // - applied by BlockquoteTextRenderer.postProcess - but still emit block margins so
38
+ // spacing between blocks matches the top-level document (a heading defaults to
39
+ // marginTop 0, so the gap before it comes from the preceding paragraph's marginBottom).
40
+ if (context.blockquoteDepth > 0) {
41
+ factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
42
+ if (builder.length > start) {
43
+ val marginTop = if (node.containsBlockImage()) config.style.imageStyle.marginTop else style.marginTop
44
+ applyMarginTop(builder, start, marginTop)
45
+ val marginBottom = if (node.containsBlockImage()) config.style.imageStyle.marginBottom else style.marginBottom
46
+ applyMarginBottom(builder, marginBottom)
47
+ }
48
+ return
49
+ }
50
+
51
+ context.setParagraphStyle(style)
52
+ try {
53
+ factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
54
+ } finally {
55
+ context.popBlockStyle()
56
+ }
57
+
58
+ if (builder.length > start) {
59
+ builder.applySpans(node, style, start)
60
+ }
61
+ }
62
+
63
+ private fun SpannableStringBuilder.applySpans(
64
+ node: TiptapASTNode,
65
+ style: ParagraphStyle,
66
+ start: Int,
67
+ ) {
68
+ val end = length
69
+
70
+ // TODO: LineHeightSpan may also clip superscript/subscript glyphs that extend
71
+ // outside the normal line bounds. A similar "skip" strategy as for images may
72
+ // be needed here once super/subscript usage in practice is better understood.
73
+ applyLineHeightSkippingImages(this, start, end, style.lineHeight)
74
+
75
+ // Only apply AlignmentSpan for non-default alignments (Center/Right)
76
+ if (style.textAlign.needsAlignmentSpan) {
77
+ setSpan(
78
+ AlignmentSpan.Standard(style.textAlign.layoutAlignment),
79
+ start,
80
+ end,
81
+ SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
82
+ )
83
+ }
84
+
85
+ val marginTop = if (node.containsBlockImage()) config.style.imageStyle.marginTop else style.marginTop
86
+ applyMarginTop(this, start, marginTop)
87
+
88
+ val marginBottom = if (node.containsBlockImage()) config.style.imageStyle.marginBottom else style.marginBottom
89
+ applyMarginBottom(this, marginBottom)
90
+ }
91
+ }
@@ -0,0 +1,148 @@
1
+ package com.renderrichtext.renderer
2
+
3
+ import android.content.Context
4
+ import android.text.SpannableStringBuilder
5
+ import com.renderrichtext.math.LatexErrorReporter
6
+ import com.renderrichtext.parser.TiptapASTNode
7
+ import com.renderrichtext.spans.ImageSpan
8
+ import com.renderrichtext.spans.MarginBottomSpan
9
+ import com.renderrichtext.styles.StyleConfig
10
+
11
+ class Renderer {
12
+ private var cachedFactory: RendererFactory? = null
13
+ private var cachedStyle: StyleConfig? = null
14
+ private var cachedContext: Context? = null
15
+ private var cachedOnLatexError: LatexErrorReporter? = null
16
+
17
+ private val collectedImageSpans = mutableListOf<ImageSpan>()
18
+ private var lastElementMarginBottom: Float = 0f
19
+
20
+ fun configure(
21
+ style: StyleConfig,
22
+ context: Context,
23
+ onLatexError: LatexErrorReporter? = null,
24
+ ) {
25
+ if (cachedStyle === style && cachedContext === context && cachedOnLatexError === onLatexError) return
26
+
27
+ cachedStyle = style
28
+ cachedContext = context
29
+ cachedOnLatexError = onLatexError
30
+ cachedFactory =
31
+ RendererFactory(
32
+ RendererConfig(style, onLatexError),
33
+ context,
34
+ ) { span -> reportImageSpan(span) }
35
+ }
36
+
37
+ fun renderDocument(
38
+ document: TiptapASTNode,
39
+ onLinkPress: ((String) -> Unit)? = null,
40
+ onLinkLongPress: ((String) -> Unit)? = null,
41
+ onFactoidPress: ((String) -> Unit)? = null,
42
+ onMentionPress: ((String, String) -> Unit)? = null,
43
+ onMentionLongPress: ((String, String) -> Unit)? = null,
44
+ ): SpannableStringBuilder =
45
+ renderContent(
46
+ document.children,
47
+ onLinkPress,
48
+ onLinkLongPress,
49
+ onFactoidPress,
50
+ onMentionPress,
51
+ onMentionLongPress,
52
+ )
53
+
54
+ /**
55
+ * Renders a flat list of sibling nodes into a fresh buffer and hands over
56
+ * ownership of it. This is the generic envelope shared by the document and
57
+ * block-segment paths: reset the factory, build the spannable, trim the trailing
58
+ * margin, and flush deferred spans (e.g. BaselineShiftSpan) after all block-level
59
+ * spans are set - see BaselineShiftRenderer for the proper long-term fix.
60
+ *
61
+ * An optional [block] decorates the pass - it enters a block style before the
62
+ * nodes render and post-processes the finished builder - letting a caller render
63
+ * content as e.g. blockquote text without this class knowing anything
64
+ * block-specific. See [BlockquoteTextRenderer].
65
+ *
66
+ * The builder is returned as-is rather than frozen into a `SpannableString`:
67
+ * the tiptap text view adopts it verbatim (see `NoCopySpannableFactory`),
68
+ * and converting it would cost a quadratic span copy for no benefit. Each call
69
+ * builds its own buffer, so callers must not mutate one they have passed on.
70
+ */
71
+ fun renderContent(
72
+ nodes: List<TiptapASTNode>,
73
+ onLinkPress: ((String) -> Unit)? = null,
74
+ onLinkLongPress: ((String) -> Unit)? = null,
75
+ onFactoidPress: ((String) -> Unit)? = null,
76
+ onMentionPress: ((String, String) -> Unit)? = null,
77
+ onMentionLongPress: ((String, String) -> Unit)? = null,
78
+ block: BlockquoteTextRenderer? = null,
79
+ ): SpannableStringBuilder {
80
+ val factory =
81
+ requireNotNull(cachedFactory) {
82
+ "Renderer must be configured with a style before rendering."
83
+ }
84
+
85
+ factory.resetForNewRender()
86
+ factory.onMentionPress = onMentionPress
87
+ factory.onMentionLongPress = onMentionLongPress
88
+ collectedImageSpans.clear()
89
+ lastElementMarginBottom = 0f
90
+
91
+ val builder = SpannableStringBuilder()
92
+
93
+ block?.push(factory.blockStyleContext)
94
+ try {
95
+ factory.renderNodes(nodes, builder, onLinkPress, onLinkLongPress, onFactoidPress)
96
+ } finally {
97
+ block?.pop(factory.blockStyleContext)
98
+ }
99
+
100
+ removeTrailingMargin(builder)
101
+ block?.postProcess(builder)
102
+ factory.flushDeferredSpans(builder)
103
+
104
+ return builder
105
+ }
106
+
107
+ /** Removes trailing newlines and captures the margin of the final element. */
108
+ private fun removeTrailingMargin(builder: SpannableStringBuilder) {
109
+ if (builder.isEmpty()) return
110
+
111
+ // Identify the last margin span and store its value
112
+ val lastSpan =
113
+ builder
114
+ .getSpans(0, builder.length, MarginBottomSpan::class.java)
115
+ .maxByOrNull { builder.getSpanEnd(it) }
116
+
117
+ lastElementMarginBottom = lastSpan?.marginBottom ?: 0f
118
+
119
+ // Trim trailing newlines
120
+ while (builder.endsWith('\n')) {
121
+ builder.delete(builder.length - 1, builder.length)
122
+ }
123
+
124
+ // Clean up the span if it no longer covers any text
125
+ if (lastSpan != null && builder.getSpanEnd(lastSpan) >= builder.length) {
126
+ builder.removeSpan(lastSpan)
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Returns the marginBottom value of the last element in the document.
132
+ * This is dynamically determined from the actual last element (paragraph, image, heading, etc.)
133
+ * and can be used in MeasurementStore to adjust the measured height.
134
+ */
135
+ fun getLastElementMarginBottom(): Float = lastElementMarginBottom
136
+
137
+ /**
138
+ * Internal helper used by the Factory's lambda to collect spans.
139
+ */
140
+ private fun reportImageSpan(span: ImageSpan) {
141
+ collectedImageSpans.add(span)
142
+ }
143
+
144
+ /**
145
+ * Provides the EnrichedTiptapText with the exact list of spans that need registration.
146
+ */
147
+ fun getCollectedImageSpans(): List<ImageSpan> = collectedImageSpans
148
+ }
@@ -0,0 +1,17 @@
1
+ package com.renderrichtext.renderer
2
+
3
+ import android.text.SpannableStringBuilder
4
+ import com.renderrichtext.parser.TiptapASTNode
5
+
6
+ class SoftBreakRenderer : NodeRenderer {
7
+ override fun render(
8
+ node: TiptapASTNode,
9
+ builder: SpannableStringBuilder,
10
+ onLinkPress: ((String) -> Unit)?,
11
+ onLinkLongPress: ((String) -> Unit)?,
12
+ onFactoidPress: ((String) -> Unit)?,
13
+ factory: RendererFactory,
14
+ ) {
15
+ builder.append(" ")
16
+ }
17
+ }