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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +140 -3
  3. package/ReactNativeRenderRichText.podspec +23 -0
  4. package/android/build.gradle +86 -0
  5. package/android/consumer-rules.pro +3 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/cpp/jni-adapter.cpp +191 -0
  8. package/android/src/main/java/com/renderrichtext/EnrichedTiptapText.kt +673 -0
  9. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -0
  10. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextManager.kt +405 -0
  11. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -0
  12. package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -0
  13. package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -0
  14. package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -0
  15. package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -0
  16. package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -0
  17. package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -0
  18. package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -0
  19. package/android/src/main/java/com/renderrichtext/events/FactoidPressEvent.kt +23 -0
  20. package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -0
  21. package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -0
  22. package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -0
  23. package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -0
  24. package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -0
  25. package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -0
  26. package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -0
  27. package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -0
  28. package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -0
  29. package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -0
  30. package/android/src/main/java/com/renderrichtext/parser/TiptapASTNode.kt +69 -0
  31. package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -0
  32. package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -0
  33. package/android/src/main/java/com/renderrichtext/renderer/BlockStyleContext.kt +181 -0
  34. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -0
  35. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -0
  36. package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -0
  37. package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -0
  38. package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -0
  39. package/android/src/main/java/com/renderrichtext/renderer/FactoidRenderer.kt +97 -0
  40. package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -0
  41. package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -0
  42. package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -0
  43. package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -0
  44. package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -0
  45. package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -0
  46. package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -0
  47. package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -0
  48. package/android/src/main/java/com/renderrichtext/renderer/NodeRenderer.kt +207 -0
  49. package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -0
  50. package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -0
  51. package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -0
  52. package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -0
  53. package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -0
  54. package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -0
  55. package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -0
  56. package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -0
  57. package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -0
  58. package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -0
  59. package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -0
  60. package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -0
  61. package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -0
  62. package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -0
  63. package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -0
  64. package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -0
  65. package/android/src/main/java/com/renderrichtext/spans/FactoidBackgroundSpan.kt +265 -0
  66. package/android/src/main/java/com/renderrichtext/spans/FactoidPaddingSpan.kt +258 -0
  67. package/android/src/main/java/com/renderrichtext/spans/FactoidSpan.kt +32 -0
  68. package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -0
  69. package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -0
  70. package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -0
  71. package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -0
  72. package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -0
  73. package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -0
  74. package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -0
  75. package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -0
  76. package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -0
  77. package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -0
  78. package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -0
  79. package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -0
  80. package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -0
  81. package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -0
  82. package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -0
  83. package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -0
  84. package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -0
  85. package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -0
  86. package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -0
  87. package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -0
  88. package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -0
  89. package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -0
  90. package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -0
  91. package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -0
  92. package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -0
  93. package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -0
  94. package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -0
  95. package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -0
  96. package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -0
  97. package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -0
  98. package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -0
  99. package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -0
  100. package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -0
  101. package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -0
  102. package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -0
  103. package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -0
  104. package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -0
  105. package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -0
  106. package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -0
  107. package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -0
  108. package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -0
  109. package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -0
  110. package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -0
  111. package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -0
  112. package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -0
  113. package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -0
  114. package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -0
  115. package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -0
  116. package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -0
  117. package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -0
  118. package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -0
  119. package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -0
  120. package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -0
  121. package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -0
  122. package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -0
  123. package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -0
  124. package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -0
  125. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -0
  126. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -0
  127. package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -0
  128. package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -0
  129. package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -0
  130. package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -0
  131. package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -0
  132. package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -0
  133. package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -0
  134. package/android/src/main/java/com/renderrichtext/utils/common/TiptapViewManagerUtils.kt +242 -0
  135. package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -0
  136. package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -0
  137. package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -0
  138. package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -0
  139. package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -0
  140. package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -0
  141. package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -0
  142. package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -0
  143. package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -0
  144. package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -0
  145. package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -0
  146. package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -0
  147. package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -0
  148. package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -0
  149. package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -0
  150. package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -0
  151. package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -0
  152. package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -0
  153. package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -0
  154. package/android/src/main/java/com/renderrichtext/utils/text/view/LinkEvents.kt +88 -0
  155. package/android/src/main/java/com/renderrichtext/utils/text/view/LinkLongPressMovementMethod.kt +449 -0
  156. package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -0
  157. package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -0
  158. package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -0
  159. package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -0
  160. package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -0
  161. package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -0
  162. package/android/src/main/jni/CMakeLists.txt +52 -0
  163. package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -0
  164. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -0
  165. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -0
  166. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -0
  167. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -0
  168. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -0
  169. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -0
  170. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -0
  171. package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -0
  172. package/cpp/parser/TiptapASTNode.hpp +68 -0
  173. package/cpp/parser/TiptapParser.cpp +233 -0
  174. package/cpp/parser/TiptapParser.hpp +13 -0
  175. package/cpp/vendor/nlohmann/json.hpp +24765 -0
  176. package/ios/EnrichedTiptapText.h +27 -0
  177. package/ios/EnrichedTiptapText.mm +1222 -0
  178. package/ios/attachments/ENRMImageAttachment.h +51 -0
  179. package/ios/attachments/ENRMImageAttachment.m +398 -0
  180. package/ios/attachments/ENRMLinkPillAttachment.h +47 -0
  181. package/ios/attachments/ENRMLinkPillAttachment.m +324 -0
  182. package/ios/attachments/ENRMLinkPillIconCache.h +28 -0
  183. package/ios/attachments/ENRMLinkPillIconCache.m +181 -0
  184. package/ios/attachments/ENRMLinkPillText.h +27 -0
  185. package/ios/attachments/ENRMLinkPillText.m +118 -0
  186. package/ios/attachments/ENRMMathInlineAttachment.h +7 -0
  187. package/ios/attachments/ENRMMathInlineAttachment.m +4 -0
  188. package/ios/attachments/ThematicBreakAttachment.h +15 -0
  189. package/ios/attachments/ThematicBreakAttachment.m +33 -0
  190. package/ios/internals/ENRMViewFreeMeasurement.h +208 -0
  191. package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -0
  192. package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -0
  193. package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -0
  194. package/ios/internals/EnrichedTiptapTextState.h +24 -0
  195. package/ios/internals/MeasurementCache.h +308 -0
  196. package/ios/internals/ShadowMeasurementUtils.h +145 -0
  197. package/ios/parser/ENRMTextLinkAttributes.h +7 -0
  198. package/ios/parser/ENRMTiptapParser.h +33 -0
  199. package/ios/parser/ENRMTiptapParser.mm +73 -0
  200. package/ios/parser/TiptapASTNode.h +57 -0
  201. package/ios/parser/TiptapASTNode.m +52 -0
  202. package/ios/parser/TiptapParserBridge.mm +179 -0
  203. package/ios/renderer/AttributedRenderer.h +23 -0
  204. package/ios/renderer/AttributedRenderer.m +229 -0
  205. package/ios/renderer/BaseRenderer.h +33 -0
  206. package/ios/renderer/BaseRenderer.m +46 -0
  207. package/ios/renderer/BaselineShiftTextAttributes.h +10 -0
  208. package/ios/renderer/BaselineShiftTextAttributes.m +26 -0
  209. package/ios/renderer/BlankLineRenderer.h +4 -0
  210. package/ios/renderer/BlankLineRenderer.m +51 -0
  211. package/ios/renderer/BlockquoteRenderer.h +6 -0
  212. package/ios/renderer/BlockquoteRenderer.m +258 -0
  213. package/ios/renderer/CodeBlockRenderer.h +9 -0
  214. package/ios/renderer/CodeBlockRenderer.m +99 -0
  215. package/ios/renderer/CodeRenderer.h +9 -0
  216. package/ios/renderer/CodeRenderer.m +69 -0
  217. package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -0
  218. package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -0
  219. package/ios/renderer/EmphasisRenderer.h +5 -0
  220. package/ios/renderer/EmphasisRenderer.m +100 -0
  221. package/ios/renderer/FactoidRenderer.h +9 -0
  222. package/ios/renderer/FactoidRenderer.m +130 -0
  223. package/ios/renderer/HeadingRenderer.h +6 -0
  224. package/ios/renderer/HeadingRenderer.m +103 -0
  225. package/ios/renderer/HighlightRenderer.h +7 -0
  226. package/ios/renderer/HighlightRenderer.m +33 -0
  227. package/ios/renderer/LinkRenderer.h +6 -0
  228. package/ios/renderer/LinkRenderer.m +158 -0
  229. package/ios/renderer/ListItemRenderer.h +24 -0
  230. package/ios/renderer/ListItemRenderer.m +283 -0
  231. package/ios/renderer/ListRenderer.h +13 -0
  232. package/ios/renderer/ListRenderer.m +126 -0
  233. package/ios/renderer/MentionRenderer.h +9 -0
  234. package/ios/renderer/MentionRenderer.m +96 -0
  235. package/ios/renderer/NodeRenderer.h +8 -0
  236. package/ios/renderer/ParagraphRenderer.h +6 -0
  237. package/ios/renderer/ParagraphRenderer.m +85 -0
  238. package/ios/renderer/RenderContext.h +145 -0
  239. package/ios/renderer/RenderContext.m +403 -0
  240. package/ios/renderer/RendererFactory.h +12 -0
  241. package/ios/renderer/RendererFactory.m +158 -0
  242. package/ios/renderer/StrikethroughRenderer.h +5 -0
  243. package/ios/renderer/StrikethroughRenderer.m +30 -0
  244. package/ios/renderer/StrongRenderer.h +5 -0
  245. package/ios/renderer/StrongRenderer.m +88 -0
  246. package/ios/renderer/TextRenderer.h +6 -0
  247. package/ios/renderer/TextRenderer.m +18 -0
  248. package/ios/renderer/ThematicBreakRenderer.h +4 -0
  249. package/ios/renderer/ThematicBreakRenderer.m +42 -0
  250. package/ios/renderer/UnderlineRenderer.h +4 -0
  251. package/ios/renderer/UnderlineRenderer.m +41 -0
  252. package/ios/styles/ENRMFontSlot.h +12 -0
  253. package/ios/styles/ENRMFontSlot.m +24 -0
  254. package/ios/styles/StyleConfig.h +488 -0
  255. package/ios/styles/StyleConfig.mm +2820 -0
  256. package/ios/utils/AccessibilityInfo.h +35 -0
  257. package/ios/utils/AccessibilityInfo.m +24 -0
  258. package/ios/utils/BlockquoteBorder.h +26 -0
  259. package/ios/utils/BlockquoteBorder.m +294 -0
  260. package/ios/utils/CodeBackground.h +19 -0
  261. package/ios/utils/CodeBackground.m +197 -0
  262. package/ios/utils/CodeBlockBackground.h +17 -0
  263. package/ios/utils/CodeBlockBackground.m +103 -0
  264. package/ios/utils/ContextMenuUtils.h +76 -0
  265. package/ios/utils/ContextMenuUtils.mm +75 -0
  266. package/ios/utils/ENRMAccessibilityLabels.h +40 -0
  267. package/ios/utils/ENRMAccessibilityLabels.m +5 -0
  268. package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -0
  269. package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -0
  270. package/ios/utils/ENRMAtomicSize.h +65 -0
  271. package/ios/utils/ENRMCodeBlockContent.h +37 -0
  272. package/ios/utils/ENRMCodeBlockContent.mm +89 -0
  273. package/ios/utils/ENRMFeatureFlags.h +25 -0
  274. package/ios/utils/ENRMImageDownloader.h +30 -0
  275. package/ios/utils/ENRMImageDownloader.m +168 -0
  276. package/ios/utils/ENRMLatexErrorCoordinator.h +27 -0
  277. package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -0
  278. package/ios/utils/ENRMLatexErrorReporting.h +13 -0
  279. package/ios/utils/ENRMLinkContextMenus.h +93 -0
  280. package/ios/utils/ENRMLinkContextMenus.m +178 -0
  281. package/ios/utils/ENRMLinkRegexConfig.h +30 -0
  282. package/ios/utils/ENRMLinkRegexConfig.mm +104 -0
  283. package/ios/utils/ENRMLinkRegexProps.h +23 -0
  284. package/ios/utils/ENRMLocalImageLoader.h +35 -0
  285. package/ios/utils/ENRMLocalImageLoader.m +111 -0
  286. package/ios/utils/ENRMMenuAction.h +27 -0
  287. package/ios/utils/ENRMMenuAction.m +33 -0
  288. package/ios/utils/ENRMSpoilerOverlayManager.h +15 -0
  289. package/ios/utils/ENRMSpoilerOverlayManager.m +19 -0
  290. package/ios/utils/ENRMSpoilerTapUtils.h +6 -0
  291. package/ios/utils/ENRMSpoilerTapUtils.m +11 -0
  292. package/ios/utils/ENRMTailFadeInAnimator.h +16 -0
  293. package/ios/utils/ENRMTailFadeInAnimator.m +161 -0
  294. package/ios/utils/ENRMTextHitTest.h +23 -0
  295. package/ios/utils/ENRMTextHitTest.m +20 -0
  296. package/ios/utils/ENRMTextInteractionUtils.h +20 -0
  297. package/ios/utils/ENRMTextInteractionUtils.m +15 -0
  298. package/ios/utils/ENRMTextRenderer.h +38 -0
  299. package/ios/utils/ENRMTextRenderer.m +54 -0
  300. package/ios/utils/ENRMTextViewSetup.h +132 -0
  301. package/ios/utils/ENRMUIKit.h +429 -0
  302. package/ios/utils/EditMenuUtils.h +105 -0
  303. package/ios/utils/EditMenuUtils.m +143 -0
  304. package/ios/utils/FactoidBackground.h +20 -0
  305. package/ios/utils/FactoidBackground.m +161 -0
  306. package/ios/utils/FontScaleObserver.h +10 -0
  307. package/ios/utils/FontScaleObserver.m +55 -0
  308. package/ios/utils/FontUtils.h +31 -0
  309. package/ios/utils/FontUtils.m +63 -0
  310. package/ios/utils/HTMLGenerator.h +25 -0
  311. package/ios/utils/HTMLGenerator.m +1041 -0
  312. package/ios/utils/HeightUpdateUtils.h +25 -0
  313. package/ios/utils/ImageRequestHeaderUtils.h +35 -0
  314. package/ios/utils/LastElementUtils.h +97 -0
  315. package/ios/utils/LinkContextMenuUtils.h +55 -0
  316. package/ios/utils/LinkPillContentUtils.h +46 -0
  317. package/ios/utils/LinkTapUtils.h +56 -0
  318. package/ios/utils/LinkTapUtils.m +139 -0
  319. package/ios/utils/ListMarkerDrawer.h +15 -0
  320. package/ios/utils/ListMarkerDrawer.m +239 -0
  321. package/ios/utils/MentionBackground.h +26 -0
  322. package/ios/utils/MentionBackground.m +86 -0
  323. package/ios/utils/ParagraphStyleUtils.h +87 -0
  324. package/ios/utils/ParagraphStyleUtils.m +484 -0
  325. package/ios/utils/PasteboardUtils.h +48 -0
  326. package/ios/utils/PasteboardUtils.m +166 -0
  327. package/ios/utils/RTFExportUtils.h +24 -0
  328. package/ios/utils/RTFExportUtils.m +299 -0
  329. package/ios/utils/RuntimeKeys.h +44 -0
  330. package/ios/utils/RuntimeKeys.m +13 -0
  331. package/ios/utils/SelectionColorUtils.h +14 -0
  332. package/ios/utils/StylePropsUtils.h +1340 -0
  333. package/ios/utils/TaskListTapUtils.h +6 -0
  334. package/ios/utils/TaskListTapUtils.m +14 -0
  335. package/ios/utils/TextViewLayoutManager.h +14 -0
  336. package/ios/utils/TextViewLayoutManager.mm +198 -0
  337. package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -0
  338. package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -0
  339. package/ios/utils/TiptapExtractor.h +17 -0
  340. package/ios/utils/TiptapExtractor.m +367 -0
  341. package/ios/views/ENRMTiptapTextView.h +25 -0
  342. package/ios/views/ENRMTiptapTextView.m +247 -0
  343. package/package.json +70 -6
  344. package/react-native.config.js +10 -0
  345. package/src/EnrichedTiptapTextNativeComponent.ts +739 -0
  346. package/src/RichText.tsx +164 -0
  347. package/src/admonitionDefaults.ts +58 -0
  348. package/src/darkModeTextStyle.ts +99 -0
  349. package/src/headingDefaults.ts +21 -0
  350. package/src/index.tsx +4 -0
  351. package/src/linkVariantUtils.ts +120 -0
  352. package/src/normalizeTiptapStyle.ts +397 -0
  353. package/src/rn-globals.d.ts +1 -0
  354. package/src/styleUtils.ts +107 -0
  355. package/src/types/TiptapStyle.ts +532 -0
  356. package/src/types/TiptapStyleInternal.ts +299 -0
  357. package/src/types/linkPill.ts +9 -0
  358. package/src/utils/isDev.ts +4 -0
@@ -0,0 +1,168 @@
1
+ #import "ENRMImageDownloader.h"
2
+ #import "ENRMImageAttachment.h"
3
+ #import "ENRMLocalImageLoader.h"
4
+ #import <CommonCrypto/CommonDigest.h>
5
+ #include <TargetConditionals.h>
6
+
7
+ static const NSUInteger kDiskCacheMemoryCapacity = 10 * 1024 * 1024;
8
+ static const NSUInteger kDiskCacheDiskCapacity = 100 * 1024 * 1024;
9
+
10
+ NSUInteger ENRMImageByteCost(RCTUIImage *image)
11
+ {
12
+ CGImageRef cgImage = image.CGImage;
13
+ if (!cgImage)
14
+ return 0;
15
+ return CGImageGetBytesPerRow(cgImage) * CGImageGetHeight(cgImage);
16
+ }
17
+
18
+ static NSDictionary<NSString *, NSString *> *ENRMNormalizedImageHeaders(NSDictionary<NSString *, NSString *> *headers)
19
+ {
20
+ NSMutableDictionary<NSString *, NSString *> *normalized = [NSMutableDictionary dictionary];
21
+ for (NSString *name in [headers.allKeys sortedArrayUsingSelector:@selector(compare:)])
22
+ normalized[name.lowercaseString] = headers[name];
23
+ return normalized;
24
+ }
25
+
26
+ NSString *ENRMImageCacheKey(NSString *url, NSDictionary<NSString *, NSString *> *headers)
27
+ {
28
+ if (headers.count == 0) {
29
+ return url;
30
+ }
31
+ NSDictionary<NSString *, NSString *> *normalized = ENRMNormalizedImageHeaders(headers);
32
+ NSArray<NSString *> *names = [normalized.allKeys sortedArrayUsingSelector:@selector(compare:)];
33
+ NSMutableArray<NSString *> *pairs = [NSMutableArray arrayWithCapacity:names.count];
34
+ for (NSString *name in names) {
35
+ [pairs addObject:[NSString stringWithFormat:@"%@:%@", name, normalized[name]]];
36
+ }
37
+ NSString *joined = [pairs componentsJoinedByString:@"\n"];
38
+ NSData *data = [joined dataUsingEncoding:NSUTF8StringEncoding];
39
+ unsigned char digest[CC_SHA256_DIGEST_LENGTH];
40
+ CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
41
+ NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
42
+ for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
43
+ [hash appendFormat:@"%02x", digest[i]];
44
+ }
45
+ return [NSString stringWithFormat:@"%@|%@", url, hash];
46
+ }
47
+
48
+ @implementation ENRMImageDownloader {
49
+ NSURLSession *_session;
50
+ NSURLSession *_headerSession;
51
+ NSMutableDictionary<NSString *, NSMutableArray<ENRMImageDownloadCompletion> *> *_inFlightRequests;
52
+ }
53
+
54
+ + (instancetype)shared
55
+ {
56
+ static ENRMImageDownloader *instance;
57
+ static dispatch_once_t onceToken;
58
+ dispatch_once(&onceToken, ^{ instance = [[ENRMImageDownloader alloc] init]; });
59
+ return instance;
60
+ }
61
+
62
+ - (instancetype)init
63
+ {
64
+ self = [super init];
65
+ if (self) {
66
+ NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
67
+ config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:kDiskCacheMemoryCapacity
68
+ diskCapacity:kDiskCacheDiskCapacity
69
+ directoryURL:nil];
70
+ config.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
71
+ config.timeoutIntervalForRequest = 15;
72
+ config.timeoutIntervalForResource = 30;
73
+ _session = [NSURLSession sessionWithConfiguration:config];
74
+ // No URL cache: it keys by URL alone and would mix header sets.
75
+ NSURLSessionConfiguration *headerConfig = [config copy];
76
+ headerConfig.URLCache = nil;
77
+ _headerSession = [NSURLSession sessionWithConfiguration:headerConfig];
78
+ _inFlightRequests = [NSMutableDictionary dictionary];
79
+ }
80
+ return self;
81
+ }
82
+
83
+ - (void)downloadURL:(NSString *)url
84
+ headers:(NSDictionary<NSString *, NSString *> *)headers
85
+ completion:(ENRMImageDownloadCompletion)completion
86
+ {
87
+ if (url.length == 0) {
88
+ completion(nil);
89
+ return;
90
+ }
91
+
92
+ headers = ENRMNormalizedImageHeaders(headers);
93
+ BOOL isLocal = ENRMIsLocalImageURL(url);
94
+ NSString *cacheKey = isLocal ? url : ENRMImageCacheKey(url, headers);
95
+
96
+ RCTUIImage *cached = [[ENRMImageAttachment originalImageCache] objectForKey:cacheKey];
97
+ if (cached) {
98
+ completion(cached);
99
+ return;
100
+ }
101
+
102
+ if (isLocal) {
103
+ RCTUIImage *local = ENRMLoadLocalImage(url);
104
+ if (local) {
105
+ [[ENRMImageAttachment originalImageCache] setObject:local forKey:cacheKey cost:ENRMImageByteCost(local)];
106
+ }
107
+ completion(local);
108
+ return;
109
+ }
110
+
111
+ @synchronized(_inFlightRequests) {
112
+ NSMutableArray *existing = _inFlightRequests[cacheKey];
113
+ if (existing) {
114
+ [existing addObject:completion];
115
+ return;
116
+ }
117
+ _inFlightRequests[cacheKey] = [NSMutableArray arrayWithObject:completion];
118
+ }
119
+
120
+ NSURL *nsURL = [NSURL URLWithString:url];
121
+ if (!nsURL) {
122
+ [self dispatchCallbacksForKey:cacheKey image:nil];
123
+ return;
124
+ }
125
+
126
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nsURL];
127
+ [headers enumerateKeysAndObjectsUsingBlock:^(NSString *name, NSString *value, BOOL *stop) {
128
+ [request setValue:value forHTTPHeaderField:name];
129
+ }];
130
+
131
+ NSURLSession *session = headers.count > 0 ? _headerSession : _session;
132
+ [[session dataTaskWithRequest:request
133
+ completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
134
+ #if !TARGET_OS_OSX
135
+ RCTUIImage *image = (data && !error) ? [RCTUIImage imageWithData:data] : nil;
136
+ #else
137
+ RCTUIImage *image = (data && !error) ? [[RCTUIImage alloc] initWithData:data] : nil;
138
+ #endif
139
+
140
+ if (image) {
141
+ [[ENRMImageAttachment originalImageCache] setObject:image
142
+ forKey:cacheKey
143
+ cost:ENRMImageByteCost(image)];
144
+ }
145
+
146
+ [self dispatchCallbacksForKey:cacheKey image:image];
147
+ }] resume];
148
+ }
149
+
150
+ - (void)dispatchCallbacksForKey:(NSString *)cacheKey image:(RCTUIImage *_Nullable)image
151
+ {
152
+ NSArray<ENRMImageDownloadCompletion> *callbacks;
153
+ @synchronized(_inFlightRequests) {
154
+ callbacks = [_inFlightRequests[cacheKey] copy];
155
+ [_inFlightRequests removeObjectForKey:cacheKey];
156
+ }
157
+
158
+ if (!callbacks)
159
+ return;
160
+
161
+ dispatch_async(dispatch_get_main_queue(), ^{
162
+ for (ENRMImageDownloadCompletion cb in callbacks) {
163
+ cb(image);
164
+ }
165
+ });
166
+ }
167
+
168
+ @end
@@ -0,0 +1,27 @@
1
+ #pragma once
2
+ #import <Foundation/Foundation.h>
3
+
4
+ #import "ENRMLatexErrorReporting.h"
5
+
6
+ NS_ASSUME_NONNULL_BEGIN
7
+
8
+ /// Returns YES when the error was delivered to a live event emitter, NO when it
9
+ /// must be queued and flushed once an emitter is attached.
10
+ typedef BOOL (^ENRMLatexErrorEmit)(NSString *source, NSString *message, BOOL displayMode);
11
+
12
+ /// Shared LaTeX-error plumbing for both host views. Owns the dedup set and the
13
+ /// pending queue and wires reporters; the only per-view difference - casting
14
+ /// `_eventEmitter` to the concrete type - stays in the `emit` block each view
15
+ /// supplies.
16
+ @interface ENRMLatexErrorCoordinator : NSObject
17
+
18
+ - (instancetype)initWithEmit:(ENRMLatexErrorEmit)emit NS_DESIGNATED_INITIALIZER;
19
+ - (instancetype)init NS_UNAVAILABLE;
20
+
21
+ - (void)wireReporters:(NSArray<id<ENRMLatexErrorReporting>> *)reporters;
22
+ - (void)reportSource:(NSString *)source message:(NSString *)message displayMode:(BOOL)displayMode;
23
+ - (void)flushPending;
24
+
25
+ @end
26
+
27
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,75 @@
1
+ #import "ENRMLatexErrorCoordinator.h"
2
+
3
+ @interface ENRMPendingLatexError : NSObject
4
+ @property (nonatomic, copy, readonly) NSString *source;
5
+ @property (nonatomic, copy, readonly) NSString *message;
6
+ @property (nonatomic, readonly) BOOL displayMode;
7
+ @end
8
+
9
+ @implementation ENRMPendingLatexError
10
+ - (instancetype)initWithSource:(NSString *)source message:(NSString *)message displayMode:(BOOL)displayMode
11
+ {
12
+ if (self = [super init]) {
13
+ _source = [source copy];
14
+ _message = [message copy];
15
+ _displayMode = displayMode;
16
+ }
17
+ return self;
18
+ }
19
+ @end
20
+
21
+ @implementation ENRMLatexErrorCoordinator {
22
+ ENRMLatexErrorEmit _emit;
23
+ NSMutableSet<NSString *> *_reportedErrors;
24
+ NSMutableArray<ENRMPendingLatexError *> *_pendingErrors;
25
+ }
26
+
27
+ - (instancetype)initWithEmit:(ENRMLatexErrorEmit)emit
28
+ {
29
+ if (self = [super init]) {
30
+ _emit = [emit copy];
31
+ _reportedErrors = [NSMutableSet set];
32
+ _pendingErrors = [NSMutableArray array];
33
+ }
34
+ return self;
35
+ }
36
+
37
+ - (void)wireReporters:(NSArray<id<ENRMLatexErrorReporting>> *)reporters
38
+ {
39
+ if (reporters.count == 0)
40
+ return;
41
+ __weak __typeof(self) weakSelf = self;
42
+ ENRMLatexErrorHandler handler = ^(NSString *source, NSString *message, BOOL displayMode) {
43
+ [weakSelf reportSource:source message:message displayMode:displayMode];
44
+ };
45
+ for (id<ENRMLatexErrorReporting> reporter in reporters) {
46
+ reporter.onLatexError = handler;
47
+ [reporter reportLatexErrorIfNeeded];
48
+ }
49
+ }
50
+
51
+ - (void)reportSource:(NSString *)source message:(NSString *)message displayMode:(BOOL)displayMode
52
+ {
53
+ NSString *key = [NSString stringWithFormat:@"%@ %@", displayMode ? @"B" : @"I", source];
54
+ if ([_reportedErrors containsObject:key])
55
+ return;
56
+ [_reportedErrors addObject:key];
57
+ if (!_emit(source, message, displayMode)) {
58
+ [_pendingErrors addObject:[[ENRMPendingLatexError alloc] initWithSource:source
59
+ message:message
60
+ displayMode:displayMode]];
61
+ }
62
+ }
63
+
64
+ - (void)flushPending
65
+ {
66
+ if (_pendingErrors.count == 0)
67
+ return;
68
+ NSArray<ENRMPendingLatexError *> *pending = [_pendingErrors copy];
69
+ [_pendingErrors removeAllObjects];
70
+ for (ENRMPendingLatexError *e in pending) {
71
+ _emit(e.source, e.message, e.displayMode);
72
+ }
73
+ }
74
+
75
+ @end
@@ -0,0 +1,13 @@
1
+ #pragma once
2
+ #import <Foundation/Foundation.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ typedef void (^ENRMLatexErrorHandler)(NSString *source, NSString *message, BOOL displayMode);
7
+
8
+ @protocol ENRMLatexErrorReporting <NSObject>
9
+ @property (nonatomic, copy, nullable) ENRMLatexErrorHandler onLatexError;
10
+ - (void)reportLatexErrorIfNeeded;
11
+ @end
12
+
13
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,93 @@
1
+ #pragma once
2
+ #import "ENRMUIKit.h"
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ /// One item of a link's long-press menu.
7
+ @interface ENRMLinkContextMenuItem : NSObject
8
+ @property (nonatomic, copy) NSString *text;
9
+ /// SF Symbol name; empty for no icon.
10
+ @property (nonatomic, copy) NSString *icon;
11
+ @property (nonatomic, assign) BOOL disabled;
12
+ @property (nonatomic, assign) BOOL destructive;
13
+ @end
14
+
15
+ /// The menu items for links whose URL matches `pattern`.
16
+ @interface ENRMLinkContextMenuEntry : NSObject
17
+ @property (nonatomic, copy) NSString *pattern;
18
+ @property (nonatomic, copy) NSArray<ENRMLinkContextMenuItem *> *items;
19
+ @end
20
+
21
+ typedef void (^ENRMLinkContextMenuPressHandler)(NSString *url, NSString *pattern, NSString *itemText);
22
+
23
+ /**
24
+ * The `linkContextMenuItems` prop: long-press menus for links, by URL pattern.
25
+ * Entries are tried in order, like link variants, and the first whose pattern
26
+ * matches a URL supplies its menu. Menus need iOS 17; below that no URL has one.
27
+ */
28
+ @interface ENRMLinkContextMenus : NSObject
29
+ @property (nonatomic, copy) NSArray<ENRMLinkContextMenuEntry *> *entries;
30
+ @property (nonatomic, copy, nullable) ENRMLinkContextMenuPressHandler onPress;
31
+ - (BOOL)hasMenuForURL:(nullable NSString *)url;
32
+ #if !TARGET_OS_OSX
33
+ /// `title` is what the user sees the link as: its text, or its pill label.
34
+ - (nullable UIMenu *)menuForURL:(nullable NSString *)url title:(nullable NSString *)title;
35
+ #endif
36
+ @end
37
+
38
+ #if !TARGET_OS_OSX
39
+ /// Adopted by a text view's delegate to supply the long-press menus of its links.
40
+ @protocol ENRMLinkContextMenuSource <NSObject>
41
+ - (nullable ENRMLinkContextMenus *)linkContextMenusForTextView:(UITextView *)textView;
42
+ @end
43
+
44
+ /// Adopted by a text view that presents the menus of some of its links itself.
45
+ @protocol ENRMLinkMenuPresenting <NSObject>
46
+ /// UIKit must be given no menu for such a link, or it lifts the link as a text item as well.
47
+ - (BOOL)presentsLinkMenuAtIndex:(NSUInteger)index;
48
+ @end
49
+
50
+ /**
51
+ * The copy of a link that its long-press menu lifts.
52
+ *
53
+ * UIKit draws a menu's preview only from a view that is on screen, and hides that view for as
54
+ * long as the menu is open. Lifting part of a larger view through a visible path would hide
55
+ * all of it, and a detached view stays blank until the menu appears. So the link is lifted as
56
+ * an image of itself, laid over the view it belongs to and taken down when the menu ends.
57
+ */
58
+ @interface ENRMLinkMenuLift : NSObject
59
+ /// Called when the image is taken down, for the owner to restore what the image stood in for.
60
+ @property (nonatomic, copy, nullable) void (^onEnd)(void);
61
+ /// Whether an image is laid over `view`.
62
+ - (BOOL)isOverView:(UIView *)view;
63
+ /// Takes down the image laid before, if any.
64
+ - (void)layImage:(UIImage *)image atFrame:(CGRect)frame overView:(UIView *)view;
65
+ /// The preview that lifts the laid image, nil when there is none. A nil `backgroundColor`
66
+ /// leaves UIKit's own behind the image.
67
+ - (nullable UITargetedPreview *)previewWithCornerRadius:(CGFloat)cornerRadius
68
+ backgroundColor:(nullable UIColor *)backgroundColor;
69
+ - (void)end;
70
+ /// Ends once the menu has animated away, unless another image was laid in the meantime.
71
+ - (void)endWithAnimator:(nullable id<UIContextMenuInteractionAnimating>)animator;
72
+ @end
73
+
74
+ #ifdef __cplusplus
75
+ extern "C" {
76
+ #endif
77
+
78
+ /**
79
+ * What a long press on a text item shows: the link's configured menu, or what links did before
80
+ * menus existed (the system menu with a preview, or `onLinkLongPress` when previews are off).
81
+ * Every text view delegate answers `textView:menuConfigurationForTextItem:defaultMenu:` with this.
82
+ * A link whose menu the text view presents itself (`ENRMLinkMenuPresenting`) gets none here.
83
+ */
84
+ UITextItemMenuConfiguration *_Nullable ENRMLinkMenuConfigurationForTextItem(
85
+ UITextView *textView, UITextItem *textItem, UIMenu *defaultMenu, ENRMLinkContextMenus *_Nullable menus,
86
+ BOOL linkPreviewEnabled, void (^_Nullable onLinkLongPress)(NSString *url)) API_AVAILABLE(ios(17.0));
87
+
88
+ #ifdef __cplusplus
89
+ }
90
+ #endif
91
+ #endif
92
+
93
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,178 @@
1
+ #import "ENRMLinkContextMenus.h"
2
+ #import "LinkTapUtils.h"
3
+
4
+ @implementation ENRMLinkContextMenuItem
5
+ @end
6
+
7
+ @implementation ENRMLinkContextMenuEntry
8
+ @end
9
+
10
+ @implementation ENRMLinkContextMenus {
11
+ NSArray<NSRegularExpression *> *_regexes;
12
+ // A long press asks about the same URL several times (gesture gating, then the menu).
13
+ NSMutableDictionary<NSString *, id> *_entriesByURL;
14
+ }
15
+
16
+ - (instancetype)init
17
+ {
18
+ if (self = [super init]) {
19
+ _entries = @[];
20
+ _regexes = @[];
21
+ _entriesByURL = [NSMutableDictionary new];
22
+ }
23
+ return self;
24
+ }
25
+
26
+ - (void)setEntries:(NSArray<ENRMLinkContextMenuEntry *> *)entries
27
+ {
28
+ NSMutableArray<ENRMLinkContextMenuEntry *> *valid = [NSMutableArray arrayWithCapacity:entries.count];
29
+ NSMutableArray<NSRegularExpression *> *regexes = [NSMutableArray arrayWithCapacity:entries.count];
30
+ for (ENRMLinkContextMenuEntry *entry in entries) {
31
+ NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:entry.pattern options:0 error:nil];
32
+ if (!regex || entry.items.count == 0)
33
+ continue;
34
+ [valid addObject:entry];
35
+ [regexes addObject:regex];
36
+ }
37
+ _entries = [valid copy];
38
+ _regexes = [regexes copy];
39
+ [_entriesByURL removeAllObjects];
40
+ }
41
+
42
+ - (nullable ENRMLinkContextMenuEntry *)entryForURL:(NSString *)url
43
+ {
44
+ if (url.length == 0 || _entries.count == 0)
45
+ return nil;
46
+ id cached = _entriesByURL[url];
47
+ if (cached)
48
+ return cached == NSNull.null ? nil : cached;
49
+
50
+ ENRMLinkContextMenuEntry *match = nil;
51
+ NSRange whole = NSMakeRange(0, url.length);
52
+ for (NSUInteger index = 0; index < _regexes.count; index++) {
53
+ if ([_regexes[index] rangeOfFirstMatchInString:url options:0 range:whole].location != NSNotFound) {
54
+ match = _entries[index];
55
+ break;
56
+ }
57
+ }
58
+ _entriesByURL[url] = match ?: NSNull.null;
59
+ return match;
60
+ }
61
+
62
+ - (BOOL)hasMenuForURL:(NSString *)url
63
+ {
64
+ #if !TARGET_OS_OSX
65
+ if (@available(iOS 17.0, *))
66
+ return [self entryForURL:url] != nil;
67
+ #endif
68
+ return NO;
69
+ }
70
+
71
+ #if !TARGET_OS_OSX
72
+ - (UIMenu *)menuForURL:(NSString *)url title:(NSString *)title
73
+ {
74
+ if (![self hasMenuForURL:url])
75
+ return nil;
76
+ ENRMLinkContextMenuEntry *entry = [self entryForURL:url];
77
+ NSString *pattern = entry.pattern;
78
+ NSMutableArray<UIAction *> *actions = [NSMutableArray arrayWithCapacity:entry.items.count];
79
+ __weak ENRMLinkContextMenus *weakSelf = self;
80
+ for (ENRMLinkContextMenuItem *item in entry.items) {
81
+ NSString *text = item.text;
82
+ UIAction *action = [UIAction actionWithTitle:text
83
+ image:item.icon.length > 0 ? [UIImage systemImageNamed:item.icon] : nil
84
+ identifier:nil
85
+ handler:^(__kindof UIAction *pressed) {
86
+ ENRMLinkContextMenuPressHandler onPress = weakSelf.onPress;
87
+ if (onPress)
88
+ onPress(url, pattern, text);
89
+ }];
90
+ if (item.disabled)
91
+ action.attributes |= UIMenuElementAttributesDisabled;
92
+ if (item.destructive)
93
+ action.attributes |= UIMenuElementAttributesDestructive;
94
+ [actions addObject:action];
95
+ }
96
+ return [UIMenu menuWithTitle:title ?: @"" children:actions];
97
+ }
98
+ #endif
99
+ @end
100
+
101
+ #if !TARGET_OS_OSX
102
+ @implementation ENRMLinkMenuLift {
103
+ UIImageView *_imageView;
104
+ }
105
+
106
+ - (BOOL)isOverView:(UIView *)view
107
+ {
108
+ return _imageView != nil && _imageView.superview == view;
109
+ }
110
+
111
+ - (void)layImage:(UIImage *)image atFrame:(CGRect)frame overView:(UIView *)view
112
+ {
113
+ [self end];
114
+ _imageView = [[UIImageView alloc] initWithImage:image];
115
+ _imageView.frame = frame;
116
+ [view addSubview:_imageView];
117
+ }
118
+
119
+ - (UITargetedPreview *)previewWithCornerRadius:(CGFloat)cornerRadius backgroundColor:(UIColor *)backgroundColor
120
+ {
121
+ if (_imageView.window == nil)
122
+ return nil;
123
+ UIPreviewParameters *parameters = [[UIPreviewParameters alloc] init];
124
+ if (backgroundColor)
125
+ parameters.backgroundColor = backgroundColor;
126
+ parameters.visiblePath = [UIBezierPath bezierPathWithRoundedRect:_imageView.bounds cornerRadius:cornerRadius];
127
+ return [[UITargetedPreview alloc] initWithView:_imageView parameters:parameters];
128
+ }
129
+
130
+ - (void)end
131
+ {
132
+ if (!_imageView)
133
+ return;
134
+ [_imageView removeFromSuperview];
135
+ _imageView = nil;
136
+ if (self.onEnd)
137
+ self.onEnd();
138
+ }
139
+
140
+ - (void)endWithAnimator:(id<UIContextMenuInteractionAnimating>)animator
141
+ {
142
+ UIImageView *laid = _imageView;
143
+ if (!laid)
144
+ return;
145
+ __weak ENRMLinkMenuLift *weakSelf = self;
146
+ void (^end)(void) = ^{
147
+ ENRMLinkMenuLift *strongSelf = weakSelf;
148
+ if (strongSelf && strongSelf->_imageView == laid)
149
+ [strongSelf end];
150
+ };
151
+ if (animator)
152
+ [animator addCompletion:end];
153
+ else
154
+ end();
155
+ }
156
+
157
+ @end
158
+
159
+ UITextItemMenuConfiguration *ENRMLinkMenuConfigurationForTextItem(UITextView *textView, UITextItem *textItem,
160
+ UIMenu *defaultMenu, ENRMLinkContextMenus *menus,
161
+ BOOL linkPreviewEnabled,
162
+ void (^onLinkLongPress)(NSString *url))
163
+ {
164
+ if ([textView conformsToProtocol:@protocol(ENRMLinkMenuPresenting)] &&
165
+ [(id<ENRMLinkMenuPresenting>)textView presentsLinkMenuAtIndex:textItem.range.location])
166
+ return nil;
167
+ NSString *url = linkURLAtRange(textView, textItem.range);
168
+ UIMenu *menu = [menus menuForURL:url title:linkTitleAtIndex(textView.textStorage, textItem.range.location)];
169
+ if (menu)
170
+ return [UITextItemMenuConfiguration configurationWithMenu:menu];
171
+ if (url && !linkPreviewEnabled) {
172
+ if (onLinkLongPress)
173
+ onLinkLongPress(url);
174
+ return nil;
175
+ }
176
+ return [UITextItemMenuConfiguration configurationWithMenu:defaultMenu];
177
+ }
178
+ #endif
@@ -0,0 +1,30 @@
1
+ #pragma once
2
+
3
+ #import <Foundation/Foundation.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface ENRMLinkRegexConfig : NSObject
8
+
9
+ @property (nonatomic, copy, readonly) NSString *pattern;
10
+ @property (nonatomic, assign, readonly) BOOL caseInsensitive;
11
+ @property (nonatomic, assign, readonly) BOOL dotAll;
12
+ @property (nonatomic, assign, readonly) BOOL isDisabled;
13
+ @property (nonatomic, assign, readonly) BOOL isDefault;
14
+ @property (nonatomic, strong, readonly, nullable) NSRegularExpression *parsedRegex;
15
+ @property (nonatomic, strong, readonly, nullable) NSRegularExpression *parsedWholeSpanRegex;
16
+
17
+ - (instancetype)initWithPattern:(NSString *)pattern
18
+ caseInsensitive:(BOOL)caseInsensitive
19
+ dotAll:(BOOL)dotAll
20
+ isDisabled:(BOOL)isDisabled
21
+ isDefault:(BOOL)isDefault;
22
+
23
+ /// Shared instance per pattern and flags; the renderer's props go through here. Thread-safe.
24
+ + (instancetype)cachedConfigWithPattern:(NSString *)pattern caseInsensitive:(BOOL)caseInsensitive dotAll:(BOOL)dotAll;
25
+
26
+ - (BOOL)isEqualToConfig:(nullable ENRMLinkRegexConfig *)other;
27
+
28
+ @end
29
+
30
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,104 @@
1
+ #import "ENRMLinkRegexConfig.h"
2
+
3
+ #import <React/RCTLog.h>
4
+
5
+ @implementation ENRMLinkRegexConfig {
6
+ NSRegularExpression *_parsedWholeSpanRegex;
7
+ BOOL _wholeSpanResolved;
8
+ }
9
+
10
+ - (instancetype)initWithPattern:(NSString *)pattern
11
+ caseInsensitive:(BOOL)caseInsensitive
12
+ dotAll:(BOOL)dotAll
13
+ isDisabled:(BOOL)isDisabled
14
+ isDefault:(BOOL)isDefault
15
+ {
16
+ self = [super init];
17
+ if (!self) {
18
+ return nil;
19
+ }
20
+
21
+ _pattern = [pattern copy];
22
+ _caseInsensitive = caseInsensitive;
23
+ _dotAll = dotAll;
24
+ _isDisabled = isDisabled;
25
+ _isDefault = isDefault;
26
+ _parsedRegex = nil;
27
+
28
+ if (!_isDefault && !_isDisabled && _pattern.length > 0) {
29
+ NSError *error = nil;
30
+ NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:_pattern
31
+ options:[self options]
32
+ error:&error];
33
+ if (error) {
34
+ RCTLogWarn(@"[EnrichedTiptap]: The link regex '%@' is not a valid NSRegularExpression and is ignored.",
35
+ _pattern);
36
+ } else {
37
+ _parsedRegex = regex;
38
+ }
39
+ }
40
+
41
+ return self;
42
+ }
43
+
44
+ + (instancetype)cachedConfigWithPattern:(NSString *)pattern caseInsensitive:(BOOL)caseInsensitive dotAll:(BOOL)dotAll
45
+ {
46
+ static NSCache<NSString *, ENRMLinkRegexConfig *> *cache;
47
+ static dispatch_once_t onceToken;
48
+ dispatch_once(&onceToken, ^{
49
+ cache = [[NSCache alloc] init];
50
+ cache.countLimit = 64;
51
+ });
52
+ NSString *key = [NSString stringWithFormat:@"%d%d%@", caseInsensitive, dotAll, pattern];
53
+ ENRMLinkRegexConfig *config = [cache objectForKey:key];
54
+ if (!config) {
55
+ config = [[self alloc] initWithPattern:pattern
56
+ caseInsensitive:caseInsensitive
57
+ dotAll:dotAll
58
+ isDisabled:NO
59
+ isDefault:NO];
60
+ [cache setObject:config forKey:key];
61
+ }
62
+ return config;
63
+ }
64
+
65
+ - (NSRegularExpressionOptions)options
66
+ {
67
+ NSRegularExpressionOptions options = 0;
68
+ if (_caseInsensitive) {
69
+ options |= NSRegularExpressionCaseInsensitive;
70
+ }
71
+ if (_dotAll) {
72
+ options |= NSRegularExpressionDotMatchesLineSeparators;
73
+ }
74
+ return options;
75
+ }
76
+
77
+ - (NSRegularExpression *)parsedWholeSpanRegex
78
+ {
79
+ if (_parsedRegex == nil) {
80
+ return nil;
81
+ }
82
+ @synchronized(self) {
83
+ if (!_wholeSpanResolved) {
84
+ // Anchor the whole alternation so `a|ab` matches "ab" in full.
85
+ _parsedWholeSpanRegex =
86
+ [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"\\A(?:%@)\\z", _pattern]
87
+ options:[self options]
88
+ error:nil];
89
+ _wholeSpanResolved = YES;
90
+ }
91
+ return _parsedWholeSpanRegex;
92
+ }
93
+ }
94
+
95
+ - (BOOL)isEqualToConfig:(ENRMLinkRegexConfig *)other
96
+ {
97
+ if (other == nil) {
98
+ return NO;
99
+ }
100
+ return [_pattern isEqualToString:other.pattern] && _caseInsensitive == other.caseInsensitive &&
101
+ _dotAll == other.dotAll && _isDefault == other.isDefault && _isDisabled == other.isDisabled;
102
+ }
103
+
104
+ @end