@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,484 @@
1
+ #import "ParagraphStyleUtils.h"
2
+ #import "ENRMFeatureFlags.h"
3
+ #import "ENRMImageAttachment.h"
4
+ #import "ENRMLinkPillAttachment.h"
5
+ #import "LastElementUtils.h"
6
+ #import <React/RCTI18nUtil.h>
7
+
8
+ #if ENRICHED_TIPTAP_MATH
9
+ #import "ENRMMathInlineAttachment.h"
10
+ #endif
11
+
12
+ NSAttributedString *kNewlineAttributedString;
13
+ static NSParagraphStyle *kBlockSpacerTemplate;
14
+
15
+ NSLineBreakStrategy ENRMResolveLineBreakStrategy(NSString *strategy)
16
+ {
17
+ if ([strategy isEqualToString:@"standard"]) {
18
+ return NSLineBreakStrategyStandard;
19
+ } else if ([strategy isEqualToString:@"hangul-word"]) {
20
+ return NSLineBreakStrategyHangulWordPriority;
21
+ } else if ([strategy isEqualToString:@"push-out"]) {
22
+ return NSLineBreakStrategyPushOut;
23
+ }
24
+ return NSLineBreakStrategyNone;
25
+ }
26
+
27
+ NSLineBreakMode ENRMResolveEllipsizeLineBreakMode(NSString *mode)
28
+ {
29
+ if ([mode isEqualToString:@"head"]) {
30
+ return NSLineBreakByTruncatingHead;
31
+ } else if ([mode isEqualToString:@"middle"]) {
32
+ return NSLineBreakByTruncatingMiddle;
33
+ } else if ([mode isEqualToString:@"clip"]) {
34
+ return NSLineBreakByClipping;
35
+ }
36
+ return NSLineBreakByTruncatingTail;
37
+ }
38
+
39
+ __attribute__((constructor)) static void initParagraphStyleUtils(void)
40
+ {
41
+ kNewlineAttributedString = [[NSAttributedString alloc] initWithString:@"\n"];
42
+
43
+ NSMutableParagraphStyle *template = [[NSMutableParagraphStyle alloc] init];
44
+ template.minimumLineHeight = 1;
45
+ template.maximumLineHeight = 1;
46
+ kBlockSpacerTemplate = [template copy];
47
+ }
48
+
49
+ ENRMWritingDirectionMode ENRMResolveWritingDirectionMode(NSString *value)
50
+ {
51
+ if ([value isEqualToString:@"ltr"]) {
52
+ return ENRMWritingDirectionModeLTR;
53
+ }
54
+ if ([value isEqualToString:@"rtl"]) {
55
+ return ENRMWritingDirectionModeRTL;
56
+ }
57
+ if ([value isEqualToString:@"auto"]) {
58
+ return ENRMWritingDirectionModeAuto;
59
+ }
60
+ return ENRMWritingDirectionModeFirstStrong;
61
+ }
62
+
63
+ static BOOL ENRMIsStrongRTLChar(unichar c)
64
+ {
65
+ return (c >= 0x0590 && c <= 0x08FF) || (c >= 0xFB1D && c <= 0xFDFF) || (c >= 0xFE70 && c <= 0xFEFF);
66
+ }
67
+
68
+ NSWritingDirection ENRMFirstStrongDirection(NSString *text)
69
+ {
70
+ NSCharacterSet *letters = [NSCharacterSet letterCharacterSet];
71
+ NSUInteger length = text.length;
72
+ for (NSUInteger i = 0; i < length; i++) {
73
+ unichar c = [text characterAtIndex:i];
74
+ if (![letters characterIsMember:c]) {
75
+ continue;
76
+ }
77
+ return ENRMIsStrongRTLChar(c) ? NSWritingDirectionRightToLeft : NSWritingDirectionLeftToRight;
78
+ }
79
+ return NSWritingDirectionNatural;
80
+ }
81
+
82
+ BOOL ENRMParagraphIsRTL(NSParagraphStyle *style)
83
+ {
84
+ if (style && style.baseWritingDirection != NSWritingDirectionNatural) {
85
+ return style.baseWritingDirection == NSWritingDirectionRightToLeft;
86
+ }
87
+ return [[RCTI18nUtil sharedInstance] isRTL];
88
+ }
89
+
90
+ void ENRMApplyWritingDirectionMode(NSMutableAttributedString *output, ENRMWritingDirectionMode mode,
91
+ NSWritingDirection layoutDirection)
92
+ {
93
+ switch (mode) {
94
+ case ENRMWritingDirectionModeAuto:
95
+ ENRMApplyWritingDirectionToParagraphStyles(output, NSWritingDirectionNatural);
96
+ break;
97
+ case ENRMWritingDirectionModeLTR:
98
+ ENRMApplyWritingDirectionToParagraphStyles(output, NSWritingDirectionLeftToRight);
99
+ break;
100
+ case ENRMWritingDirectionModeRTL:
101
+ ENRMApplyWritingDirectionToParagraphStyles(output, NSWritingDirectionRightToLeft);
102
+ break;
103
+ case ENRMWritingDirectionModeFirstStrong:
104
+ ENRMApplyFirstStrongParagraphDirections(output, layoutDirection);
105
+ break;
106
+ }
107
+ }
108
+
109
+ void ENRMApplyFirstStrongParagraphDirections(NSMutableAttributedString *output, NSWritingDirection fallback)
110
+ {
111
+ if (output.length == 0) {
112
+ return;
113
+ }
114
+ NSString *string = output.string;
115
+ NSUInteger length = string.length;
116
+ NSUInteger position = 0;
117
+ while (position < length) {
118
+ NSRange paragraphRange = [string paragraphRangeForRange:NSMakeRange(position, 0)];
119
+ if (NSMaxRange(paragraphRange) <= position) {
120
+ break;
121
+ }
122
+ position = NSMaxRange(paragraphRange);
123
+
124
+ NSNumber *isCodeBlock = [output attribute:CodeBlockAttributeName
125
+ atIndex:paragraphRange.location
126
+ effectiveRange:nil];
127
+ if (isCodeBlock.boolValue) {
128
+ continue;
129
+ }
130
+
131
+ NSWritingDirection direction = ENRMFirstStrongDirection([string substringWithRange:paragraphRange]);
132
+ if (direction == NSWritingDirectionNatural) {
133
+ direction = fallback;
134
+ }
135
+ if (direction == NSWritingDirectionNatural) {
136
+ continue;
137
+ }
138
+
139
+ [output enumerateAttribute:NSParagraphStyleAttributeName
140
+ inRange:paragraphRange
141
+ options:0
142
+ usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL *stop) {
143
+ if (style != nil && style.baseWritingDirection == direction) {
144
+ return;
145
+ }
146
+ NSMutableParagraphStyle *updated =
147
+ style ? [style mutableCopy] : [[NSMutableParagraphStyle alloc] init];
148
+ updated.baseWritingDirection = direction;
149
+ [output addAttribute:NSParagraphStyleAttributeName value:updated range:range];
150
+ }];
151
+ }
152
+ }
153
+
154
+ void ENRMApplyWritingDirectionToParagraphStyles(NSMutableAttributedString *output, NSWritingDirection writingDirection)
155
+ {
156
+ if (output.length == 0) {
157
+ return;
158
+ }
159
+ [output enumerateAttribute:NSParagraphStyleAttributeName
160
+ inRange:NSMakeRange(0, output.length)
161
+ options:0
162
+ usingBlock:^(NSParagraphStyle *style, NSRange range, BOOL *stop) {
163
+ if (!style) {
164
+ return;
165
+ }
166
+ // Skip runs already in the target direction (issue #739),
167
+ // mirroring ENRMApplyFirstStrongParagraphDirections.
168
+ if (style.baseWritingDirection == writingDirection) {
169
+ return;
170
+ }
171
+ NSNumber *isCodeBlock = [output attribute:CodeBlockAttributeName
172
+ atIndex:range.location
173
+ effectiveRange:nil];
174
+ if (isCodeBlock.boolValue) {
175
+ return;
176
+ }
177
+ NSMutableParagraphStyle *mutable = [style mutableCopy];
178
+ mutable.baseWritingDirection = writingDirection;
179
+ [output addAttribute:NSParagraphStyleAttributeName value:mutable range:range];
180
+ }];
181
+ }
182
+
183
+ NSMutableParagraphStyle *getOrCreateParagraphStyle(NSMutableAttributedString *output, NSUInteger index)
184
+ {
185
+ NSParagraphStyle *existing = [output attribute:NSParagraphStyleAttributeName atIndex:index effectiveRange:NULL];
186
+ NSMutableParagraphStyle *style = existing ? [existing mutableCopy] : [[NSMutableParagraphStyle alloc] init];
187
+ return style;
188
+ }
189
+
190
+ void applyParagraphSpacingAfter(NSMutableAttributedString *output, NSUInteger start, CGFloat marginBottom)
191
+ {
192
+ [output appendAttributedString:kNewlineAttributedString];
193
+
194
+ NSMutableParagraphStyle *style = getOrCreateParagraphStyle(output, start);
195
+ style.paragraphSpacing = marginBottom;
196
+
197
+ NSRange range = NSMakeRange(start, output.length - start);
198
+ [output addAttribute:NSParagraphStyleAttributeName value:style range:range];
199
+ }
200
+
201
+ NSUInteger applyParagraphSpacingBefore(NSMutableAttributedString *output, NSRange range, CGFloat marginTop)
202
+ {
203
+ if (marginTop <= 0 || range.location == 0)
204
+ return 0;
205
+
206
+ NSUInteger prevIdx = range.location - 1;
207
+ if ([output.string characterAtIndex:prevIdx] != '\n')
208
+ return 0;
209
+
210
+ NSParagraphStyle *prevStyle = [output attribute:NSParagraphStyleAttributeName atIndex:prevIdx effectiveRange:NULL];
211
+
212
+ CGFloat prevMarginBottom = prevStyle.paragraphSpacing;
213
+ if (prevMarginBottom >= marginTop)
214
+ return 0;
215
+
216
+ CGFloat extraGap = marginTop - prevMarginBottom;
217
+
218
+ NSMutableParagraphStyle *spacerStyle = [kBlockSpacerTemplate mutableCopy];
219
+ spacerStyle.paragraphSpacing = MAX(0, extraGap - 1.0);
220
+
221
+ NSDictionary *attrs = @{NSParagraphStyleAttributeName : spacerStyle};
222
+ NSAttributedString *spacer = [[NSAttributedString alloc] initWithString:@"\n" attributes:attrs];
223
+
224
+ [output insertAttributedString:spacer atIndex:range.location];
225
+ return 1;
226
+ }
227
+
228
+ NSUInteger applyBlockSpacingBefore(NSMutableAttributedString *output, NSUInteger insertionPoint, CGFloat marginTop)
229
+ {
230
+ if (marginTop <= 0) {
231
+ return 0;
232
+ }
233
+
234
+ CGFloat spacing = marginTop;
235
+
236
+ // At index 0 the spacer \n produces a 1pt line fragment (minimumLineHeight=1)
237
+ // on top of paragraphSpacing. Subtract it so the total equals the desired margin.
238
+ // For non-zero positions the 1pt is intentional inter-block spacing.
239
+ if (insertionPoint == 0) {
240
+ spacing = MAX(0, marginTop - 1.0);
241
+ }
242
+
243
+ NSMutableParagraphStyle *spacerStyle = [kBlockSpacerTemplate mutableCopy];
244
+ spacerStyle.paragraphSpacing = spacing;
245
+
246
+ NSAttributedString *spacer =
247
+ [[NSAttributedString alloc] initWithString:@"\n" attributes:@{NSParagraphStyleAttributeName : spacerStyle}];
248
+
249
+ [output insertAttributedString:spacer atIndex:insertionPoint];
250
+ return 1;
251
+ }
252
+
253
+ void applyBlockSpacingAfter(NSMutableAttributedString *output, CGFloat marginBottom)
254
+ {
255
+ if (marginBottom <= 0) {
256
+ return;
257
+ }
258
+
259
+ NSUInteger spacerLocation = output.length;
260
+ [output appendAttributedString:kNewlineAttributedString];
261
+
262
+ NSMutableParagraphStyle *spacerStyle = [kBlockSpacerTemplate mutableCopy];
263
+ spacerStyle.paragraphSpacing = marginBottom;
264
+
265
+ [output addAttribute:NSParagraphStyleAttributeName value:spacerStyle range:NSMakeRange(spacerLocation, 1)];
266
+ }
267
+
268
+ CGFloat ENRMLineHeightWithLinkPills(NSAttributedString *text, NSRange range, CGFloat lineHeight)
269
+ {
270
+ #if !TARGET_OS_OSX
271
+ __block CGFloat fitting = lineHeight;
272
+ [text enumerateAttribute:NSAttachmentAttributeName
273
+ inRange:range
274
+ options:0
275
+ usingBlock:^(id value, __unused NSRange subrange, __unused BOOL *stop) {
276
+ if ([value isKindOfClass:ENRMLinkPillAttachment.class])
277
+ fitting = MAX(fitting, ((ENRMLinkPillAttachment *)value).lineHeight);
278
+ }];
279
+ return fitting;
280
+ #else
281
+ return lineHeight;
282
+ #endif
283
+ }
284
+
285
+ // UIKit's own key for the pre-substitution font. Not exported by any SDK header, so it is spelled out
286
+ // here; it is the same string UIKit writes into a UITextView's storage when it substitutes a font.
287
+ static NSString *const ENRMOriginalFontAttributeName = @"NSOriginalFont";
288
+
289
+ void ENRMPinLineMetricsToStyledFonts(NSMutableAttributedString *output, NSRange range)
290
+ {
291
+ if (range.length == 0 || NSMaxRange(range) > output.length) {
292
+ return;
293
+ }
294
+
295
+ [output enumerateAttribute:NSFontAttributeName
296
+ inRange:range
297
+ options:0
298
+ usingBlock:^(UIFont *font, NSRange fontRange, __unused BOOL *stop) {
299
+ if (font) {
300
+ [output addAttribute:ENRMOriginalFontAttributeName value:font range:fontRange];
301
+ }
302
+ }];
303
+ }
304
+
305
+ // Floor, not clamp: minimumLineHeight keeps short lines at lineHeight, while maximumLineHeight = 0 lets
306
+ // a line grow to fit a taller run (large inline code, math, images) instead of clipping it.
307
+ // We can diverge from RN's clamp because we measure the real laid-out height, so grown lines are reserved.
308
+ void ENRMApplyLineHeightToParagraphStyle(NSMutableParagraphStyle *style, CGFloat lineHeight)
309
+ {
310
+ if (lineHeight <= 0) {
311
+ return;
312
+ }
313
+
314
+ style.minimumLineHeight = lineHeight;
315
+ style.maximumLineHeight = 0;
316
+ }
317
+
318
+ void applyLineHeight(NSMutableAttributedString *output, NSRange range, CGFloat lineHeight)
319
+ {
320
+ lineHeight = ENRMLineHeightWithLinkPills(output, range, lineHeight);
321
+ if (lineHeight <= 0) {
322
+ return;
323
+ }
324
+
325
+ NSMutableParagraphStyle *style = getOrCreateParagraphStyle(output, range.location);
326
+ ENRMApplyLineHeightToParagraphStyle(style, lineHeight);
327
+
328
+ [output addAttribute:NSParagraphStyleAttributeName value:style range:range];
329
+ }
330
+
331
+ // Calculates the offset needed to center the text within the line height. Returns 0
332
+ // when the range has no minimum line height or its content already fills the line.
333
+ CGFloat calculateBaselineOffset(NSAttributedString *output, NSRange range)
334
+ {
335
+ if (range.length == 0) {
336
+ return 0;
337
+ }
338
+
339
+ // applyLineHeight floors lines with minimumLineHeight (maximumLineHeight is 0 for grown lines,
340
+ // or equal to the minimum for re-clamped code blocks), so the target is the minimum line height.
341
+ __block CGFloat targetLineHeight = 0;
342
+ [output enumerateAttribute:NSParagraphStyleAttributeName
343
+ inRange:range
344
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
345
+ usingBlock:^(NSParagraphStyle *paragraphStyle, __unused NSRange subrange, __unused BOOL *stop) {
346
+ if (!paragraphStyle) {
347
+ return;
348
+ }
349
+ targetLineHeight = MAX(paragraphStyle.minimumLineHeight, targetLineHeight);
350
+ }];
351
+
352
+ if (targetLineHeight <= 0) {
353
+ return 0;
354
+ }
355
+
356
+ // Center on real text; on a math-only line center the math box instead of its font.
357
+ __block CGFloat textLineHeight = 0;
358
+ #if ENRICHED_TIPTAP_MATH
359
+ __block BOOL hasMath = NO;
360
+ #endif
361
+ [output enumerateAttributesInRange:range
362
+ options:0
363
+ usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, __unused NSRange subrange,
364
+ __unused BOOL *stop) {
365
+ #if ENRICHED_TIPTAP_MATH
366
+ if ([attrs[NSAttachmentAttributeName] isKindOfClass:[ENRMMathInlineAttachment class]]) {
367
+ hasMath = YES;
368
+ return;
369
+ }
370
+ #endif
371
+ UIFont *font = attrs[NSFontAttributeName];
372
+ if (font) {
373
+ textLineHeight = MAX(UIFontLineHeight(font), textLineHeight);
374
+ }
375
+ }];
376
+
377
+ CGFloat contentLineHeight = textLineHeight;
378
+ #if !TARGET_OS_OSX
379
+ __block CGFloat pillBoxHeight = 0;
380
+ [output enumerateAttribute:NSAttachmentAttributeName
381
+ inRange:range
382
+ options:0
383
+ usingBlock:^(id value, NSRange subrange, BOOL *stop) {
384
+ if ([value isKindOfClass:ENRMLinkPillAttachment.class])
385
+ pillBoxHeight = MAX(pillBoxHeight, ((ENRMLinkPillAttachment *)value).boxHeight);
386
+ }];
387
+ contentLineHeight = MAX(contentLineHeight, pillBoxHeight);
388
+ #endif
389
+
390
+ #if ENRICHED_TIPTAP_MATH
391
+ // Math only grows the content height, so measure it (parsing LaTeX) only when text
392
+ // alone wouldn't already fill the line.
393
+ if (hasMath && targetLineHeight > textLineHeight) {
394
+ __block CGFloat mathBoxHeight = 0;
395
+ [output enumerateAttribute:NSAttachmentAttributeName
396
+ inRange:range
397
+ options:0
398
+ usingBlock:^(id value, __unused NSRange subrange, __unused BOOL *stop) {
399
+ if ([value isKindOfClass:[ENRMMathInlineAttachment class]]) {
400
+ mathBoxHeight = MAX(((ENRMMathInlineAttachment *)value).boxHeight, mathBoxHeight);
401
+ }
402
+ }];
403
+ contentLineHeight = MAX(contentLineHeight, mathBoxHeight);
404
+ }
405
+ #endif
406
+
407
+ if (contentLineHeight <= 0 || targetLineHeight <= contentLineHeight) {
408
+ return 0;
409
+ }
410
+
411
+ return (targetLineHeight - contentLineHeight) / 2.0;
412
+ }
413
+
414
+ // Centers text within its line height (see calculateBaselineOffset). Called per block over its
415
+ // own range so blocks can keep different line heights. Ranges that already carry a baseline offset
416
+ // are left untouched, keeping nesting (e.g. a blockquote wrapping list items) idempotent.
417
+ void applyBaselineOffset(NSMutableAttributedString *output, NSRange range)
418
+ {
419
+ CGFloat baseLineOffset = calculateBaselineOffset(output, range);
420
+ if (baseLineOffset <= 0) {
421
+ return;
422
+ }
423
+
424
+ [output enumerateAttribute:NSBaselineOffsetAttributeName
425
+ inRange:range
426
+ options:0
427
+ usingBlock:^(NSNumber *existingOffset, NSRange subrange, BOOL *stop) {
428
+ if (existingOffset != nil) {
429
+ return;
430
+ }
431
+ [output enumerateAttribute:NSAttachmentAttributeName
432
+ inRange:subrange
433
+ options:0
434
+ usingBlock:^(id attachment, NSRange attachmentRange, BOOL *innerStop) {
435
+ if ([attachment isKindOfClass:[ENRMImageAttachment class]] &&
436
+ !((ENRMImageAttachment *)attachment).isInline) {
437
+ return;
438
+ }
439
+ [output addAttribute:NSBaselineOffsetAttributeName
440
+ value:@(baseLineOffset)
441
+ range:attachmentRange];
442
+ }];
443
+ }];
444
+ }
445
+
446
+ void applyTextAlignment(NSMutableAttributedString *output, NSRange range, NSTextAlignment textAlign)
447
+ {
448
+ NSMutableParagraphStyle *style = getOrCreateParagraphStyle(output, range.location);
449
+ style.alignment = textAlign;
450
+ [output addAttribute:NSParagraphStyleAttributeName value:style range:range];
451
+ }
452
+
453
+ void ENRMApplyLineBreakStrategyToParagraphStyles(NSMutableAttributedString *output,
454
+ NSLineBreakStrategy lineBreakStrategy)
455
+ {
456
+ if (output.length == 0) {
457
+ return;
458
+ }
459
+ [output enumerateAttribute:NSParagraphStyleAttributeName
460
+ inRange:NSMakeRange(0, output.length)
461
+ options:0
462
+ usingBlock:^(NSParagraphStyle *value, NSRange range, BOOL *stop) {
463
+ if (!value) {
464
+ return;
465
+ }
466
+ NSMutableParagraphStyle *mutable = [value mutableCopy];
467
+ mutable.lineBreakStrategy = lineBreakStrategy;
468
+ [output addAttribute:NSParagraphStyleAttributeName value:mutable range:range];
469
+ }];
470
+ }
471
+
472
+ NSTextAlignment textAlignmentFromString(NSString *textAlign)
473
+ {
474
+ if ([textAlign isEqualToString:@"left"]) {
475
+ return NSTextAlignmentLeft;
476
+ } else if ([textAlign isEqualToString:@"center"]) {
477
+ return NSTextAlignmentCenter;
478
+ } else if ([textAlign isEqualToString:@"right"]) {
479
+ return NSTextAlignmentRight;
480
+ } else if ([textAlign isEqualToString:@"justify"]) {
481
+ return NSTextAlignmentJustified;
482
+ }
483
+ return NSTextAlignmentNatural;
484
+ }
@@ -0,0 +1,48 @@
1
+ #pragma once
2
+ #import "ENRMUIKit.h"
3
+ #import <Foundation/Foundation.h>
4
+
5
+ @class StyleConfig;
6
+
7
+ static NSString *const kUTIPlainText = @"public.utf8-plain-text";
8
+ static NSString *const kUTIHTML = @"public.html";
9
+ static NSString *const kUTITiptap = @"net.daringfireball.tiptap";
10
+
11
+ NS_ASSUME_NONNULL_BEGIN
12
+
13
+ #ifdef __cplusplus
14
+ extern "C" {
15
+ #endif
16
+
17
+ /**
18
+ * Copies a plain string to the platform pasteboard.
19
+ */
20
+ void copyStringToPasteboard(NSString *string);
21
+
22
+ /// Copies a { UTI → NSString | NSData } dictionary to the platform pasteboard.
23
+ void copyItemsToPasteboard(NSDictionary<NSString *, id> *items);
24
+
25
+ /**
26
+ * Copies attributed string to pasteboard with multiple representations
27
+ * (plain text, Tiptap, HTML, RTFD, RTF). Receiving apps pick the richest format they support.
28
+ */
29
+ void copyAttributedStringToPasteboard(NSAttributedString *attributedString, NSString *_Nullable tiptap,
30
+ StyleConfig *_Nullable styleConfig);
31
+
32
+ /**
33
+ * Extracts tiptap for the given range.
34
+ * Full selection returns cached tiptap; partial selection reverse-engineers from attributes.
35
+ */
36
+ NSString *_Nullable tiptapForRange(NSAttributedString *attributedText, NSRange range,
37
+ NSString *_Nullable cachedTiptap);
38
+
39
+ /**
40
+ * Returns remote image URLs (http/https only) from ENRMImageAttachments in the given range.
41
+ */
42
+ NSArray<NSString *> *imageURLsInRange(NSAttributedString *attributedText, NSRange range);
43
+
44
+ #ifdef __cplusplus
45
+ }
46
+ #endif
47
+
48
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,166 @@
1
+ #import "PasteboardUtils.h"
2
+ #import "ENRMImageAttachment.h"
3
+ #import "ENRMLinkPillText.h"
4
+ #import "HTMLGenerator.h"
5
+ #import "TiptapExtractor.h"
6
+ #import "RTFExportUtils.h"
7
+ #import "StyleConfig.h"
8
+ #include <TargetConditionals.h>
9
+ #if !TARGET_OS_OSX
10
+ #import <UIKit/UIPasteboard.h>
11
+ #endif
12
+
13
+ static NSString *const kUTIRTFD = @"com.apple.rtfd";
14
+ static NSString *const kUTIFlatRTFD = @"com.apple.flat-rtfd";
15
+ static NSString *const kUTIRTF = @"public.rtf";
16
+
17
+ #pragma mark - Private Helpers
18
+
19
+ static void addRTFData(NSMutableDictionary *items, NSAttributedString *attributedString, NSRange range,
20
+ NSString *documentType, NSString *uti)
21
+ {
22
+ NSError *error = nil;
23
+ NSData *data = [attributedString dataFromRange:range
24
+ documentAttributes:@{NSDocumentTypeDocumentAttribute : documentType}
25
+ error:&error];
26
+ if (data && !error) {
27
+ items[uti] = data;
28
+ }
29
+ }
30
+
31
+ static void addRTFDData(NSMutableDictionary *items, NSAttributedString *attributedString, NSRange range)
32
+ {
33
+ NSError *error = nil;
34
+ NSFileWrapper *wrapper =
35
+ [attributedString fileWrapperFromRange:range
36
+ documentAttributes:@{NSDocumentTypeDocumentAttribute : NSRTFDTextDocumentType}
37
+ error:&error];
38
+ if (wrapper && !error) {
39
+ NSData *data = [wrapper serializedRepresentation];
40
+ if (data) {
41
+ items[kUTIFlatRTFD] = data;
42
+ }
43
+ }
44
+ }
45
+
46
+ static void addHTMLData(NSMutableDictionary *items, NSAttributedString *attributedString, StyleConfig *styleConfig)
47
+ {
48
+ NSString *html = generateHTML(attributedString, styleConfig);
49
+ if (html) {
50
+ NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];
51
+ if (data) {
52
+ items[kUTIHTML] = data;
53
+ }
54
+ }
55
+ }
56
+
57
+ #pragma mark - Public API
58
+
59
+ void copyStringToPasteboard(NSString *string)
60
+ {
61
+ #if !TARGET_OS_OSX
62
+ [[UIPasteboard generalPasteboard] setString:string];
63
+ #else
64
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
65
+ [pasteboard clearContents];
66
+ [pasteboard setString:string forType:kUTIPlainText];
67
+ #endif
68
+ }
69
+
70
+ void copyItemsToPasteboard(NSDictionary<NSString *, id> *items)
71
+ {
72
+ #if !TARGET_OS_OSX
73
+ [[UIPasteboard generalPasteboard] setItems:@[ items ]];
74
+ #else
75
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
76
+ [pasteboard clearContents];
77
+ for (NSString *type in items) {
78
+ id value = items[type];
79
+ if ([value isKindOfClass:[NSString class]]) {
80
+ [pasteboard setString:value forType:type];
81
+ } else if ([value isKindOfClass:[NSData class]]) {
82
+ [pasteboard setData:value forType:type];
83
+ }
84
+ }
85
+ #endif
86
+ }
87
+
88
+ void copyAttributedStringToPasteboard(NSAttributedString *attributedString, NSString *_Nullable tiptap,
89
+ StyleConfig *_Nullable styleConfig)
90
+ {
91
+ if (!attributedString || attributedString.length == 0)
92
+ return;
93
+
94
+ // Expanded once here for the plain-text flavor; the exporters below then have nothing left to expand.
95
+ attributedString = ENRMAttributedStringByExpandingLinkPills(attributedString, NULL);
96
+
97
+ NSMutableDictionary *items = [NSMutableDictionary dictionary];
98
+
99
+ items[kUTIPlainText] = attributedString.string;
100
+
101
+ if (tiptap.length > 0) {
102
+ items[kUTITiptap] = tiptap;
103
+ }
104
+
105
+ if (styleConfig) {
106
+ addHTMLData(items, attributedString, styleConfig);
107
+ }
108
+
109
+ // RTF export requires preprocessing (backgrounds, markers, normalized spacing)
110
+ NSAttributedString *rtfPrepared = prepareAttributedStringForRTFExport(attributedString, styleConfig);
111
+ NSRange rtfRange = NSMakeRange(0, rtfPrepared.length);
112
+
113
+ addRTFDData(items, rtfPrepared, rtfRange);
114
+ addRTFData(items, rtfPrepared, rtfRange, NSRTFTextDocumentType, kUTIRTF);
115
+
116
+ copyItemsToPasteboard(items);
117
+ }
118
+
119
+ #pragma mark - Content Extraction
120
+
121
+ NSString *_Nullable tiptapForRange(NSAttributedString *attributedText, NSRange range,
122
+ NSString *_Nullable cachedTiptap)
123
+ {
124
+ if (!cachedTiptap || range.length == 0)
125
+ return nil;
126
+
127
+ if (!attributedText || range.location >= attributedText.length)
128
+ return nil;
129
+
130
+ range.length = MIN(range.length, attributedText.length - range.location);
131
+
132
+ // Full selection: use cached tiptap directly
133
+ BOOL isFullSelection = (range.location == 0 && range.length >= attributedText.length - 1);
134
+ if (isFullSelection) {
135
+ return cachedTiptap;
136
+ }
137
+
138
+ // Partial selection: reverse-engineer from attributes
139
+ return extractTiptapFromAttributedString(attributedText, range);
140
+ }
141
+
142
+ NSArray<NSString *> *imageURLsInRange(NSAttributedString *attributedText, NSRange range)
143
+ {
144
+ if (!attributedText || range.location == NSNotFound || range.length == 0 || range.location >= attributedText.length) {
145
+ return @[];
146
+ }
147
+
148
+ range.length = MIN(range.length, attributedText.length - range.location);
149
+
150
+ NSMutableArray<NSString *> *urls = [NSMutableArray array];
151
+
152
+ [attributedText enumerateAttribute:NSAttachmentAttributeName
153
+ inRange:range
154
+ options:0
155
+ usingBlock:^(id value, NSRange r, BOOL *stop) {
156
+ if (![value isKindOfClass:[ENRMImageAttachment class]])
157
+ return;
158
+
159
+ NSString *url = ((ENRMImageAttachment *)value).imageURL;
160
+ if ([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) {
161
+ [urls addObject:url];
162
+ }
163
+ }];
164
+
165
+ return urls;
166
+ }