@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,132 @@
1
+ #pragma once
2
+ #import "ENRMUIKit.h"
3
+ #import "LastElementUtils.h"
4
+ #import "StyleConfig.h"
5
+ #import "TextViewLayoutManager.h"
6
+ #import <React/RCTUtils.h>
7
+ #import <objc/runtime.h>
8
+
9
+ NS_ASSUME_NONNULL_BEGIN
10
+
11
+ static inline void ENRMAttachLayoutManager(ENRMPlatformTextView *textView, StyleConfig *_Nullable config)
12
+ {
13
+ NSLayoutManager *layoutManager = textView.layoutManager;
14
+ if (layoutManager == nil) {
15
+ return;
16
+ }
17
+ layoutManager.allowsNonContiguousLayout = NO;
18
+ object_setClass(layoutManager, [TextViewLayoutManager class]);
19
+ if (config != nil) {
20
+ [layoutManager setValue:config forKey:@"config"];
21
+ }
22
+ }
23
+
24
+ static inline void ENRMDetachLayoutManager(ENRMPlatformTextView *textView)
25
+ {
26
+ NSLayoutManager *layoutManager = textView.layoutManager;
27
+ if (layoutManager != nil && [object_getClass(layoutManager) isEqual:[TextViewLayoutManager class]]) {
28
+ [layoutManager setValue:nil forKey:@"config"];
29
+ object_setClass(layoutManager, [NSLayoutManager class]);
30
+ }
31
+ }
32
+
33
+ /// Returns maxWidth for soft-wrapped content, otherwise the tight width of its
34
+ /// lines, or fallbackWidth when no line fragment was laid out. The tight width
35
+ /// adds each line's head and tail indent to its used rect, which TextKit leaves
36
+ /// out; lists and code blocks re-wrap without them.
37
+ static inline CGFloat ENRMContentWidth(NSLayoutManager *layoutManager, CGFloat maxWidth, CGFloat fallbackWidth)
38
+ {
39
+ CGFloat tightWidth = 0;
40
+ NSTextStorage *storage = layoutManager.textStorage;
41
+ NSString *string = storage.string;
42
+ NSCharacterSet *newlines = [NSCharacterSet newlineCharacterSet];
43
+ NSUInteger glyphCount = [layoutManager numberOfGlyphs];
44
+ BOOL startsParagraph = YES;
45
+ NSUInteger glyphIndex = 0;
46
+ while (glyphIndex < glyphCount) {
47
+ NSRange lineGlyphRange;
48
+ CGRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:&lineGlyphRange];
49
+ if (lineGlyphRange.location == NSNotFound || NSMaxRange(lineGlyphRange) <= glyphIndex) {
50
+ break; // past a numberOfLines clamp: no fragment holds these glyphs
51
+ }
52
+ NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineGlyphRange actualGlyphRange:NULL];
53
+ if (lineCharRange.length == 0) {
54
+ break;
55
+ }
56
+
57
+ NSParagraphStyle *style = [storage attribute:NSParagraphStyleAttributeName
58
+ atIndex:lineCharRange.location
59
+ effectiveRange:NULL];
60
+ CGFloat headIndent = startsParagraph ? style.firstLineHeadIndent : style.headIndent;
61
+ CGFloat tailInset = style.tailIndent < 0 ? -style.tailIndent : 0;
62
+ tightWidth = MAX(tightWidth, usedRect.size.width + headIndent + tailInset);
63
+ if (tightWidth >= maxWidth) {
64
+ return maxWidth;
65
+ }
66
+
67
+ BOOL endsParagraph = [newlines characterIsMember:[string characterAtIndex:NSMaxRange(lineCharRange) - 1]];
68
+ NSUInteger nextGlyphIndex = NSMaxRange(lineGlyphRange);
69
+ if (nextGlyphIndex < glyphCount && !endsParagraph) {
70
+ return maxWidth; // soft wrap
71
+ }
72
+ startsParagraph = endsParagraph;
73
+ glyphIndex = nextGlyphIndex;
74
+ }
75
+ return tightWidth > 0 ? tightWidth : fallbackWidth;
76
+ }
77
+
78
+ /// Turns a raw TextKit layout pass into the final measured size. Shared by
79
+ /// the view-backed path (`ENRMMeasureTiptapText`) and the view-free path
80
+ /// (`ENRMMeasureTiptapViewFree`) so both measure with one algorithm.
81
+ ///
82
+ /// Steps, in order:
83
+ /// - Soft-wrapped content reports maxWidth: a tight width would let flexShrink
84
+ /// narrow the view, re-wrapping it and mismatching the measured height.
85
+ /// Content broken only by explicit newlines keeps its tight width.
86
+ /// - Optionally subtract the extra line fragment (iOS counts a trailing
87
+ /// newline's empty line fragment into usedRect). Read-only tiptap passes
88
+ /// YES; text input passes NO so trailing newlines keep a blank line (RN Text
89
+ /// parity).
90
+ /// - Add code-block padding when the last element is a code block, and the
91
+ /// trailing margin when enabled.
92
+ /// - Round up to the pixel grid of `scale` (pass `RCTScreenScale()` on main;
93
+ /// off-main callers must pass `LayoutContext::pointScaleFactor` instead —
94
+ /// `RCTScreenScale()` dispatch_syncs to main on first use, which the
95
+ /// measure path must never do, see issue #550).
96
+ static inline CGSize ENRMFinalizeMeasuredTextSize(ENRMTextLayoutResult layout, NSLayoutManager *layoutManager,
97
+ CGFloat maxWidth, StyleConfig *config, BOOL allowTrailingMargin,
98
+ CGFloat lastElementMarginBottom, BOOL subtractExtraLineFragment,
99
+ CGFloat scale)
100
+ {
101
+ CGFloat measuredWidth = ENRMContentWidth(layoutManager, maxWidth, layout.usedRect.size.width);
102
+ CGFloat measuredHeight = layout.usedRect.size.height;
103
+
104
+ if (subtractExtraLineFragment && !CGRectIsEmpty(layout.extraLineFragmentRect)) {
105
+ measuredHeight -= layout.extraLineFragmentRect.size.height;
106
+ }
107
+
108
+ if (isLastElementCodeBlock(layoutManager.textStorage)) {
109
+ measuredHeight += [config codeBlockPadding];
110
+ }
111
+
112
+ if (allowTrailingMargin && lastElementMarginBottom > 0) {
113
+ measuredHeight += lastElementMarginBottom;
114
+ }
115
+
116
+ return CGSizeMake(ceil(measuredWidth * scale) / scale, ceil(measuredHeight * scale) / scale);
117
+ }
118
+
119
+ static inline CGSize ENRMMeasureTiptapText(ENRMPlatformTextView *textView, CGFloat maxWidth, StyleConfig *config,
120
+ BOOL allowTrailingMargin, CGFloat lastElementMarginBottom)
121
+ {
122
+ NSAttributedString *text = ENRMGetAttributedText(textView);
123
+ if (text.length == 0) {
124
+ return CGSizeZero;
125
+ }
126
+
127
+ ENRMTextLayoutResult layout = ENRMMeasureTextLayout(textView, maxWidth);
128
+ return ENRMFinalizeMeasuredTextSize(layout, textView.layoutManager, maxWidth, config, allowTrailingMargin,
129
+ lastElementMarginBottom, YES, RCTScreenScale());
130
+ }
131
+
132
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,429 @@
1
+ #pragma once
2
+
3
+ #include <TargetConditionals.h>
4
+
5
+ // RCTUIKit.h is react-native-macos only. On iOS we import UIKit and define the aliases ourselves.
6
+ #if !TARGET_OS_OSX
7
+ #import <UIKit/UIKit.h>
8
+ #define RCTUIColor UIColor
9
+ #define RCTUIImage UIImage
10
+ #define RCTUIView UIView
11
+ #define RCTUIScrollView UIScrollView
12
+ #define RCTUIGraphicsImageRenderer UIGraphicsImageRenderer
13
+ #define RCTUIGraphicsImageRendererContext UIGraphicsImageRendererContext
14
+ #define RCTUIGraphicsImageRendererFormat UIGraphicsImageRendererFormat
15
+ #define ENRMPlatformTextView UITextView
16
+ #define ENRMTapRecognizer UITapGestureRecognizer
17
+
18
+ // Inline helpers that RCTUIKit.h normally provides on iOS.
19
+ static inline UIBezierPath *UIBezierPathWithRoundedRect(CGRect rect, CGFloat cornerRadius)
20
+ {
21
+ return [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];
22
+ }
23
+ static inline void UIBezierPathAppendPath(UIBezierPath *path, UIBezierPath *appendPath)
24
+ {
25
+ [path appendPath:appendPath];
26
+ }
27
+ static inline CGFloat UIFontLineHeight(UIFont *font)
28
+ {
29
+ return font.lineHeight;
30
+ }
31
+ #else
32
+ #import <React/RCTTextUIKit.h>
33
+ #import <React/RCTUIKit.h>
34
+ #import <React/RCTUITextView.h>
35
+ #define ENRMPlatformTextView RCTUITextView
36
+ #define ENRMTapRecognizer NSClickGestureRecognizer
37
+ #endif
38
+
39
+ /// Value for `usesFontLeading` on every NSLayoutManager that draws or
40
+ /// measures tiptap. NSLayoutManager defaults to YES, which adds each font's
41
+ /// built-in leading on top of the paragraph line height. React Native turns it
42
+ /// off for Text layout, and drawing and measuring must agree, or the reported
43
+ /// height and the on-screen layout drift apart. Fonts pulled in by glyph
44
+ /// fallback make this visible: Geeza Pro (Arabic) has nonzero leading, so with
45
+ /// YES every Arabic line measures taller than it renders.
46
+ /// https://github.com/react/react-native/blob/v0.86.2/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm#L237
47
+ static const BOOL ENRMLayoutManagerUsesFontLeading = NO;
48
+
49
+ /// On iOS, explicitly sets opaque=NO — without it the renderer produces an opaque backing,
50
+ /// breaking transparent backgrounds. macOS handles transparency by default.
51
+ static inline RCTUIGraphicsImageRenderer *ImageRendererForSize(CGSize size)
52
+ {
53
+ #if !TARGET_OS_OSX
54
+ RCTUIGraphicsImageRendererFormat *format = [RCTUIGraphicsImageRendererFormat preferredFormat];
55
+ format.opaque = NO;
56
+ return [[RCTUIGraphicsImageRenderer alloc] initWithSize:size format:format];
57
+ #else
58
+ return [[RCTUIGraphicsImageRenderer alloc] initWithSize:size];
59
+ #endif
60
+ }
61
+
62
+ /// NSBezierPath uses NS-prefixed enum values; UIBezierPath uses kCG-prefixed constants.
63
+ static inline void BezierPathSetRoundStyle(UIBezierPath *path)
64
+ {
65
+ #if !TARGET_OS_OSX
66
+ path.lineCapStyle = kCGLineCapRound;
67
+ path.lineJoinStyle = kCGLineJoinRound;
68
+ #else
69
+ path.lineCapStyle = NSLineCapStyleRound;
70
+ path.lineJoinStyle = NSLineJoinStyleRound;
71
+ #endif
72
+ }
73
+
74
+ /// Cross-platform line segment: NSBezierPath uses lineToPoint: instead of addLineToPoint:.
75
+ static inline void BezierPathAddLine(UIBezierPath *path, CGPoint point)
76
+ {
77
+ #if !TARGET_OS_OSX
78
+ [path addLineToPoint:point];
79
+ #else
80
+ [path lineToPoint:point];
81
+ #endif
82
+ }
83
+
84
+ /// Cross-platform quad-curve: NSBezierPath lacks addQuadCurveToPoint:, so we approximate
85
+ /// with a cubic Bezier using the standard quadratic-to-cubic conversion.
86
+ static inline void BezierPathAddQuadCurve(UIBezierPath *path, CGPoint end, CGPoint control)
87
+ {
88
+ #if !TARGET_OS_OSX
89
+ [path addQuadCurveToPoint:end controlPoint:control];
90
+ #else
91
+ CGPoint start = [path currentPoint];
92
+ [path curveToPoint:end
93
+ controlPoint1:CGPointMake(start.x + 2.0 / 3.0 * (control.x - start.x),
94
+ start.y + 2.0 / 3.0 * (control.y - start.y))
95
+ controlPoint2:CGPointMake(end.x + 2.0 / 3.0 * (control.x - end.x), end.y + 2.0 / 3.0 * (control.y - end.y))];
96
+ #endif
97
+ }
98
+
99
+ /// Cross-platform IME composition check: UITextView uses markedTextRange (nullable UITextRange);
100
+ /// NSTextView uses hasMarkedText (BOOL).
101
+ static inline BOOL ENRMHasMarkedText(ENRMPlatformTextView *textView)
102
+ {
103
+ #if !TARGET_OS_OSX
104
+ return textView.markedTextRange != nil;
105
+ #else
106
+ return textView.hasMarkedText;
107
+ #endif
108
+ }
109
+
110
+ /// Cross-platform plain text read: UITextView uses .text; NSTextView uses .string.
111
+ static inline NSString *ENRMGetPlainText(ENRMPlatformTextView *textView)
112
+ {
113
+ #if !TARGET_OS_OSX
114
+ return textView.text;
115
+ #else
116
+ return textView.string;
117
+ #endif
118
+ }
119
+
120
+ /// Cross-platform plain text write: UITextView uses .text setter; NSTextView uses setString:.
121
+ static inline void ENRMSetPlainText(ENRMPlatformTextView *textView, NSString *text)
122
+ {
123
+ #if !TARGET_OS_OSX
124
+ textView.text = text;
125
+ #else
126
+ [textView setString:text];
127
+ #endif
128
+ }
129
+
130
+ /// Cross-platform attributed text read: NSTextView exposes content via textStorage;
131
+ /// UITextView exposes it via attributedText.
132
+ static inline NSAttributedString *ENRMGetAttributedText(ENRMPlatformTextView *textView)
133
+ {
134
+ #if !TARGET_OS_OSX
135
+ return textView.attributedText;
136
+ #else
137
+ return textView.textStorage;
138
+ #endif
139
+ }
140
+
141
+ /// Cross-platform attributed text write: NSTextView uses textStorage setAttributedString:;
142
+ /// UITextView uses the attributedText property setter.
143
+ static inline void ENRMSetAttributedText(ENRMPlatformTextView *textView, NSAttributedString *text)
144
+ {
145
+ #if !TARGET_OS_OSX
146
+ textView.attributedText = text;
147
+ #else
148
+ [textView.textStorage setAttributedString:text];
149
+ #endif
150
+ }
151
+
152
+ /// Cross-platform text replacement at a given range.
153
+ /// iOS uses UITextInput protocol methods; macOS uses NSTextView's insertText:replacementRange:.
154
+ static inline void ENRMReplaceTextInRange(ENRMPlatformTextView *textView, NSString *text, NSRange range)
155
+ {
156
+ #if !TARGET_OS_OSX
157
+ UITextPosition *start = [textView positionFromPosition:textView.beginningOfDocument offset:(NSInteger)range.location];
158
+ UITextPosition *end = [textView positionFromPosition:textView.beginningOfDocument
159
+ offset:(NSInteger)NSMaxRange(range)];
160
+ [textView replaceRange:[textView textRangeFromPosition:start toPosition:end] withText:text];
161
+ #else
162
+ [textView insertText:text replacementRange:range];
163
+ #endif
164
+ }
165
+
166
+ /// Cross-platform content size update after measurement.
167
+ /// iOS UITextView has a settable contentSize; macOS NSTextView does not.
168
+ static inline void ENRMSetContentSize(ENRMPlatformTextView *textView, CGSize size)
169
+ {
170
+ #if !TARGET_OS_OSX
171
+ textView.contentSize = size;
172
+ #endif
173
+ }
174
+
175
+ /// Returns YES when the user has asked the system to minimise motion.
176
+ /// iOS: UIAccessibilityIsReduceMotionEnabled.
177
+ /// macOS: NSWorkspace.shared.accessibilityDisplayShouldReduceMotion.
178
+ /// Always NO on platforms where the API is unavailable.
179
+ static inline BOOL ENRMShouldReduceMotion(void)
180
+ {
181
+ #if !TARGET_OS_OSX
182
+ return UIAccessibilityIsReduceMotionEnabled();
183
+ #else
184
+ if (@available(macOS 10.12, *)) {
185
+ return NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion;
186
+ }
187
+ return NO;
188
+ #endif
189
+ }
190
+
191
+ /// Sets default typing attributes on the text view.
192
+ /// On macOS, RCTUITextView overrides setTypingAttributes: to use defaultTextAttributes,
193
+ /// so we must set that property as well.
194
+ static inline void ENRMSetDefaultTypingAttributes(ENRMPlatformTextView *textView, NSDictionary *attrs)
195
+ {
196
+ #if TARGET_OS_OSX
197
+ textView.defaultTextAttributes = attrs;
198
+ #endif
199
+ textView.typingAttributes = attrs;
200
+ }
201
+
202
+ /// Applies shared configuration to a text view used for tiptap input editing.
203
+ /// Handles platform differences: scroll indicators, text container insets,
204
+ /// drawsBackground (macOS). Sets editable=YES, scrollEnabled=YES.
205
+ static inline void ENRMConfigureTiptapTextInputTextView(ENRMPlatformTextView *textView)
206
+ {
207
+ textView.font = [UIFont systemFontOfSize:16.0];
208
+ textView.backgroundColor = [RCTUIColor clearColor];
209
+ textView.editable = YES;
210
+ #if !TARGET_OS_OSX
211
+ textView.scrollEnabled = YES;
212
+ textView.showsVerticalScrollIndicator = NO;
213
+ textView.showsHorizontalScrollIndicator = NO;
214
+ textView.textContainerInset = UIEdgeInsetsZero;
215
+ #else
216
+ textView.textContainerInsets = UIEdgeInsetsZero;
217
+ textView.drawsBackground = NO;
218
+ #endif
219
+ textView.textContainer.lineFragmentPadding = 0;
220
+ }
221
+
222
+ /// Cross-platform cursor color: iOS uses tintColor; macOS uses insertionPointColor.
223
+ static inline void ENRMSetCursorColor(ENRMPlatformTextView *textView, RCTUIColor *color)
224
+ {
225
+ #if !TARGET_OS_OSX
226
+ textView.tintColor = color;
227
+ #else
228
+ textView.insertionPointColor = color;
229
+ #endif
230
+ }
231
+
232
+ /// Cross-platform selection color: iOS uses tintColor (also affects the caret
233
+ /// and selection handles); macOS sets the selection background via
234
+ /// `selectedTextAttributes`. Pass `nil` to restore the system default.
235
+ static inline void ENRMSetSelectionColor(ENRMPlatformTextView *textView, RCTUIColor *color)
236
+ {
237
+ #if !TARGET_OS_OSX
238
+ textView.tintColor = color;
239
+ #else
240
+ RCTUIColor *resolved = color ?: [NSColor selectedTextBackgroundColor];
241
+ textView.selectedTextAttributes = @{NSBackgroundColorAttributeName : resolved};
242
+ #endif
243
+ }
244
+
245
+ /// Cross-platform focus: iOS uses becomeFirstResponder;
246
+ /// macOS uses makeFirstResponder: on the window.
247
+ static inline void ENRMFocusTextView(ENRMPlatformTextView *textView)
248
+ {
249
+ #if !TARGET_OS_OSX
250
+ [textView becomeFirstResponder];
251
+ #else
252
+ [textView.window makeFirstResponder:textView];
253
+ #endif
254
+ }
255
+
256
+ /// Cross-platform blur: iOS uses resignFirstResponder;
257
+ /// macOS clears first responder via the window.
258
+ static inline void ENRMBlurTextView(ENRMPlatformTextView *textView)
259
+ {
260
+ #if !TARGET_OS_OSX
261
+ [textView resignFirstResponder];
262
+ #else
263
+ [textView.window makeFirstResponder:nil];
264
+ #endif
265
+ }
266
+
267
+ /// Applies shared configuration to a text view used for tiptap rendering.
268
+ /// Handles platform differences: scroll indicators, text container insets,
269
+ /// drawsBackground (macOS), accessibilityElementsHidden (iOS).
270
+ static inline void ENRMConfigureTiptapTextView(ENRMPlatformTextView *textView)
271
+ {
272
+ textView.font = [UIFont systemFontOfSize:16.0];
273
+ textView.backgroundColor = [RCTUIColor clearColor];
274
+ textView.textColor = [RCTUIColor blackColor];
275
+ textView.editable = NO;
276
+ textView.scrollEnabled = NO;
277
+ #if !TARGET_OS_OSX
278
+ textView.showsVerticalScrollIndicator = NO;
279
+ textView.showsHorizontalScrollIndicator = NO;
280
+ textView.textContainerInset = UIEdgeInsetsZero;
281
+ #else
282
+ textView.textContainerInsets = UIEdgeInsetsZero;
283
+ textView.drawsBackground = NO;
284
+ #endif
285
+ textView.textContainer.lineFragmentPadding = 0;
286
+ textView.layoutManager.usesFontLeading = ENRMLayoutManagerUsesFontLeading;
287
+ textView.linkTextAttributes = @{};
288
+ textView.selectable = YES;
289
+ #if !TARGET_OS_OSX
290
+ textView.accessibilityElementsHidden = YES;
291
+ #endif
292
+ }
293
+
294
+ /// Result of a text layout measurement pass.
295
+ typedef struct {
296
+ CGRect usedRect;
297
+ CGRect extraLineFragmentRect;
298
+ } ENRMTextLayoutResult;
299
+
300
+ /// Measures text layout in a text view for a given width.
301
+ /// On macOS, temporarily disables widthTracksTextView so the container width
302
+ /// can be set directly without being overridden by the view's frame.
303
+ static inline ENRMTextLayoutResult ENRMMeasureTextLayout(ENRMPlatformTextView *textView, CGFloat maxWidth)
304
+ {
305
+ #if TARGET_OS_OSX
306
+ textView.textContainer.widthTracksTextView = NO;
307
+ #endif
308
+ textView.textContainer.size = CGSizeMake(maxWidth, CGFLOAT_MAX);
309
+ [textView.layoutManager ensureLayoutForTextContainer:textView.textContainer];
310
+ ENRMTextLayoutResult result;
311
+ result.usedRect = [textView.layoutManager usedRectForTextContainer:textView.textContainer];
312
+ result.extraLineFragmentRect = textView.layoutManager.extraLineFragmentRect;
313
+ #if TARGET_OS_OSX
314
+ textView.textContainer.widthTracksTextView = YES;
315
+ #endif
316
+ return result;
317
+ }
318
+
319
+ /// Cross-platform display refresh: UIView requires layoutIfNeeded before setNeedsDisplay
320
+ /// to flush pending layout before the redraw; NSView takes a BOOL argument.
321
+ /// Implemented as a macro to avoid Objective-C++ implicit pointer conversion issues in .mm files.
322
+ #if !TARGET_OS_OSX
323
+ #define ENRMSetNeedsDisplay(view) \
324
+ do { \
325
+ [(view) layoutIfNeeded]; \
326
+ [(view) setNeedsDisplay]; \
327
+ } while (0)
328
+ #else
329
+ #define ENRMSetNeedsDisplay(view) [(view) setNeedsDisplay:YES]
330
+ #endif
331
+
332
+ /// Invalidates the text container, re-lays out any existing content, and
333
+ /// triggers a redraw. On iOS also resets contentOffset to zero.
334
+ /// Does NOT touch the text view's frame — callers that need to reposition
335
+ /// the view should set the frame themselves before calling this.
336
+ static inline void ENRMRefreshTextViewLayout(ENRMPlatformTextView *textView)
337
+ {
338
+ #if !TARGET_OS_OSX
339
+ textView.contentOffset = CGPointZero;
340
+ #endif
341
+ textView.textContainer.size = CGSizeMake(textView.bounds.size.width, CGFLOAT_MAX);
342
+ NSUInteger textLength = ENRMGetAttributedText(textView).length;
343
+ if (textLength > 0) {
344
+ [textView.layoutManager invalidateLayoutForCharacterRange:NSMakeRange(0, textLength) actualCharacterRange:NULL];
345
+ [textView.layoutManager ensureLayoutForTextContainer:textView.textContainer];
346
+ }
347
+ ENRMSetNeedsDisplay(textView);
348
+ }
349
+
350
+ /// Refreshes a text view's layout and display after it is attached to a window.
351
+ /// Sets the frame and text container to the given bounds, invalidates layout for
352
+ /// any existing content, then triggers a redraw.
353
+ static inline void ENRMRefreshTextViewAfterWindowAttach(ENRMPlatformTextView *textView, CGRect bounds)
354
+ {
355
+ textView.frame = bounds;
356
+ ENRMRefreshTextViewLayout(textView);
357
+ }
358
+
359
+ /// Cross-platform text deselection: UITextView uses selectedTextRange (nullable);
360
+ /// NSTextView uses selectedRange (length-based).
361
+ static inline void ENRMClearSelection(ENRMPlatformTextView *textView)
362
+ {
363
+ #if !TARGET_OS_OSX
364
+ if (textView.selectedTextRange != nil) {
365
+ textView.selectedTextRange = nil;
366
+ }
367
+ #else
368
+ if (textView.selectedRange.length > 0) {
369
+ textView.selectedRange = NSMakeRange(0, 0);
370
+ }
371
+ #endif
372
+ }
373
+
374
+ // ── Placeholder label abstraction ──────────────────────────────────────────────
375
+
376
+ #if !TARGET_OS_OSX
377
+ typedef UILabel ENRMPlaceholderLabel;
378
+ #else
379
+ typedef NSTextField ENRMPlaceholderLabel;
380
+ #endif
381
+
382
+ /// Creates a placeholder label configured for use inside an ENRMPlatformTextView.
383
+ /// On iOS: UILabel with multiline, placeholderTextColor, auto-layout pinned to
384
+ /// textContainerInset and lineFragmentPadding.
385
+ /// On macOS: borderless, non-editable NSTextField pinned to the text view origin.
386
+ static inline ENRMPlaceholderLabel *ENRMCreatePlaceholderLabel(ENRMPlatformTextView *textView, UIFont *font)
387
+ {
388
+ #if !TARGET_OS_OSX
389
+ UILabel *label = [[UILabel alloc] init];
390
+ label.numberOfLines = 0;
391
+ label.textColor = [UIColor placeholderTextColor];
392
+ label.font = font;
393
+ label.translatesAutoresizingMaskIntoConstraints = NO;
394
+ [textView addSubview:label];
395
+ [NSLayoutConstraint activateConstraints:@[
396
+ [label.topAnchor constraintEqualToAnchor:textView.topAnchor constant:textView.textContainerInset.top],
397
+ [label.leadingAnchor constraintEqualToAnchor:textView.leadingAnchor
398
+ constant:textView.textContainer.lineFragmentPadding],
399
+ [label.trailingAnchor constraintEqualToAnchor:textView.trailingAnchor
400
+ constant:-textView.textContainer.lineFragmentPadding],
401
+ ]];
402
+ return label;
403
+ #else
404
+ NSTextField *label = [[NSTextField alloc] initWithFrame:CGRectZero];
405
+ label.bordered = NO;
406
+ label.editable = NO;
407
+ label.selectable = NO;
408
+ label.drawsBackground = NO;
409
+ label.textColor = [NSColor placeholderTextColor];
410
+ label.font = font;
411
+ label.translatesAutoresizingMaskIntoConstraints = NO;
412
+ [textView addSubview:label];
413
+ [NSLayoutConstraint activateConstraints:@[
414
+ [label.topAnchor constraintEqualToAnchor:textView.topAnchor],
415
+ [label.leadingAnchor constraintEqualToAnchor:textView.leadingAnchor],
416
+ ]];
417
+ return label;
418
+ #endif
419
+ }
420
+
421
+ /// Cross-platform placeholder attributed text setter.
422
+ static inline void ENRMSetPlaceholderAttributedText(ENRMPlaceholderLabel *label, NSAttributedString *text)
423
+ {
424
+ #if !TARGET_OS_OSX
425
+ label.attributedText = text;
426
+ #else
427
+ label.attributedStringValue = text;
428
+ #endif
429
+ }
@@ -0,0 +1,105 @@
1
+ #pragma once
2
+ #import "ENRMUIKit.h"
3
+ #import <Foundation/Foundation.h>
4
+
5
+ @class StyleConfig;
6
+
7
+ NS_ASSUME_NONNULL_BEGIN
8
+
9
+ typedef struct {
10
+ BOOL copyAsTiptap;
11
+ BOOL copyImageURL;
12
+ // The owner must keep these strings alive for the duration of the call (the
13
+ // view holds them in strong ivars).
14
+ __unsafe_unretained NSString *_Nullable copyLabel;
15
+ __unsafe_unretained NSString *_Nullable copyAsTiptapLabel;
16
+ __unsafe_unretained NSString *_Nullable copyImageUrlLabel;
17
+ __unsafe_unretained NSString *_Nullable copyImageUrlsLabel;
18
+ __unsafe_unretained NSArray<NSString *> *_Nullable copyImageUrlPluralTemplates;
19
+ } ENRMSelectionMenuConfig;
20
+
21
+ /// Resolves the "Copy Image URL(s)" menu title for a given image count.
22
+ /// Uses the precomputed plural templates (indexed by count, 0..100) when present;
23
+ /// counts > 100 use the "other" form (copyImageUrlsLabel). Otherwise the
24
+ /// singular/`{count}` templates. All labels are resolved JS-side.
25
+ static inline NSString *ENRMResolveImageURLsTitle(ENRMSelectionMenuConfig config, NSUInteger count)
26
+ {
27
+ NSArray<NSString *> *templates = config.copyImageUrlPluralTemplates;
28
+ NSString *titleTemplate;
29
+ if (count < templates.count) {
30
+ titleTemplate = templates[count];
31
+ } else if (count == 1) {
32
+ titleTemplate = config.copyImageUrlLabel;
33
+ } else {
34
+ titleTemplate = config.copyImageUrlsLabel;
35
+ }
36
+ return [titleTemplate stringByReplacingOccurrencesOfString:@"{count}" withString:[@(count) stringValue]];
37
+ }
38
+
39
+ #ifdef __cplusplus
40
+
41
+ /// Strong owners for the label strings in ENRMSelectionMenuConfig.
42
+ struct ENRMSelectionMenuLabels {
43
+ NSString *copyLabel = nil;
44
+ NSString *copyAsTiptapLabel = nil;
45
+ NSString *copyImageUrlLabel = nil;
46
+ NSString *copyImageUrlsLabel = nil;
47
+ NSArray<NSString *> *copyImageUrlPluralTemplates = nil;
48
+ };
49
+
50
+ /// Converts a codegen SelectionMenuConfig struct into strong NSString labels.
51
+ template <typename T> static inline ENRMSelectionMenuLabels ENRMParseSelectionMenuLabels(const T &src)
52
+ {
53
+ NSMutableArray<NSString *> *templates = [NSMutableArray array];
54
+ for (const auto &s : src.copyImageUrlPluralTemplates) {
55
+ [templates addObject:[[NSString alloc] initWithUTF8String:s.c_str()]];
56
+ }
57
+ return {
58
+ [[NSString alloc] initWithUTF8String:src.copyLabel.c_str()],
59
+ [[NSString alloc] initWithUTF8String:src.copyAsTiptapLabel.c_str()],
60
+ [[NSString alloc] initWithUTF8String:src.copyImageUrlLabel.c_str()],
61
+ [[NSString alloc] initWithUTF8String:src.copyImageUrlsLabel.c_str()],
62
+ templates,
63
+ };
64
+ }
65
+
66
+ /// Builds an ENRMSelectionMenuConfig from pre-parsed labels and boolean flags.
67
+ static inline ENRMSelectionMenuConfig ENRMBuildSelectionMenuConfig(const ENRMSelectionMenuLabels &labels,
68
+ bool copyAsTiptap, bool copyImageUrl)
69
+ {
70
+ return (ENRMSelectionMenuConfig){
71
+ .copyAsTiptap = copyAsTiptap,
72
+ .copyImageURL = copyImageUrl,
73
+ .copyLabel = labels.copyLabel,
74
+ .copyAsTiptapLabel = labels.copyAsTiptapLabel,
75
+ .copyImageUrlLabel = labels.copyImageUrlLabel,
76
+ .copyImageUrlsLabel = labels.copyImageUrlsLabel,
77
+ .copyImageUrlPluralTemplates = labels.copyImageUrlPluralTemplates,
78
+ };
79
+ }
80
+
81
+ extern "C" {
82
+ #endif
83
+
84
+ #if !TARGET_OS_OSX
85
+ // TODO: Remove API_AVAILABLE(ios(16.0)) guard when the minimum iOS deployment target in RN is bumped to 16.
86
+ // TODO: selectionMenuConfig labels do NOT reach the system "Save to Camera Roll /
87
+ // Copy" sheet shown when long-pressing an NSTextAttachment image — UIKit owns
88
+ // that sheet end-to-end. Hooking UIContextMenuInteraction on image regions and
89
+ // returning a custom UIContextMenuConfiguration would let us relabel those items.
90
+ UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange range, NSString *_Nullable cachedTiptap,
91
+ StyleConfig *styleConfig, NSArray<UIMenuElement *> *suggestedActions,
92
+ NSArray<UIAction *> *_Nullable customActions,
93
+ ENRMSelectionMenuConfig selectionMenuConfig) API_AVAILABLE(ios(16.0));
94
+ #else
95
+ NSMenu *_Nullable buildEditMenuForSelection(NSAttributedString *attributedText, NSRange range,
96
+ NSString *_Nullable cachedTiptap, StyleConfig *styleConfig,
97
+ NSArray *suggestedActions, NSArray<NSMenuItem *> *_Nullable customItems,
98
+ ENRMSelectionMenuConfig selectionMenuConfig);
99
+ #endif
100
+
101
+ #ifdef __cplusplus
102
+ }
103
+ #endif
104
+
105
+ NS_ASSUME_NONNULL_END