@mathers101/react-native-render-rich-text 0.1.9 → 0.1.14

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 (327) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +140 -140
  3. package/ReactNativeRenderRichText.podspec +23 -23
  4. package/android/build.gradle +86 -86
  5. package/android/consumer-rules.pro +3 -3
  6. package/android/src/main/AndroidManifest.xml +2 -2
  7. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -24
  8. package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -13
  9. package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -644
  10. package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -17
  11. package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -112
  12. package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -576
  13. package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -24
  14. package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -28
  15. package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -24
  16. package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -25
  17. package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -29
  18. package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -23
  19. package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -23
  20. package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -25
  21. package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -25
  22. package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -26
  23. package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -9
  24. package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -59
  25. package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -35
  26. package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -45
  27. package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -39
  28. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -243
  29. package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -56
  30. package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -135
  31. package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -37
  32. package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -28
  33. package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -81
  34. package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -26
  35. package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -17
  36. package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -90
  37. package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -109
  38. package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -211
  39. package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -133
  40. package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -85
  41. package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -91
  42. package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -148
  43. package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -17
  44. package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -143
  45. package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -28
  46. package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -28
  47. package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -31
  48. package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -46
  49. package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -28
  50. package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -164
  51. package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -27
  52. package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -302
  53. package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -205
  54. package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -216
  55. package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -32
  56. package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -42
  57. package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -29
  58. package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -38
  59. package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -80
  60. package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -481
  61. package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -42
  62. package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -39
  63. package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -154
  64. package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -381
  65. package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -68
  66. package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -33
  67. package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -38
  68. package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -76
  69. package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -132
  70. package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -46
  71. package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -127
  72. package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -33
  73. package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -12
  74. package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -39
  75. package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -143
  76. package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -26
  77. package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -69
  78. package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -96
  79. package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -46
  80. package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -11
  81. package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -83
  82. package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -80
  83. package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -26
  84. package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -21
  85. package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -21
  86. package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -33
  87. package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -19
  88. package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -29
  89. package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -17
  90. package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -17
  91. package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -36
  92. package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -44
  93. package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -23
  94. package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -34
  95. package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -65
  96. package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -29
  97. package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -19
  98. package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -33
  99. package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -27
  100. package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -17
  101. package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -21
  102. package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -411
  103. package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -92
  104. package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -16
  105. package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -16
  106. package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -75
  107. package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -29
  108. package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -32
  109. package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -23
  110. package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -17
  111. package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -31
  112. package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -86
  113. package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -32
  114. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -36
  115. package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -42
  116. package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -27
  117. package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -10
  118. package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -9
  119. package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -31
  120. package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -56
  121. package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -18
  122. package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -28
  123. package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -6
  124. package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -368
  125. package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -88
  126. package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -203
  127. package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -167
  128. package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -84
  129. package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -1105
  130. package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -423
  131. package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -9
  132. package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -9
  133. package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -76
  134. package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -61
  135. package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -286
  136. package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -39
  137. package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -142
  138. package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -43
  139. package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -30
  140. package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -15
  141. package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -19
  142. package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -235
  143. package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -48
  144. package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -119
  145. package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -217
  146. package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -101
  147. package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -40
  148. package/android/src/main/jni/CMakeLists.txt +52 -52
  149. package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -30
  150. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -29
  151. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -45
  152. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -21
  153. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -38
  154. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -53
  155. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -9
  156. package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -24
  157. package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -40
  158. package/cpp/parser/TiptapParser.hpp +13 -13
  159. package/cpp/vendor/nlohmann/json.hpp +24765 -24765
  160. package/ios/EnrichedTiptapText.h +27 -27
  161. package/ios/attachments/ENRMImageAttachment.h +51 -51
  162. package/ios/attachments/ENRMImageAttachment.m +398 -398
  163. package/ios/attachments/ENRMLinkPillAttachment.h +47 -47
  164. package/ios/attachments/ENRMLinkPillAttachment.m +324 -324
  165. package/ios/attachments/ENRMLinkPillIconCache.h +28 -28
  166. package/ios/attachments/ENRMLinkPillIconCache.m +181 -181
  167. package/ios/attachments/ENRMLinkPillText.h +27 -27
  168. package/ios/attachments/ENRMLinkPillText.m +118 -118
  169. package/ios/attachments/ENRMMathInlineAttachment.h +7 -7
  170. package/ios/attachments/ENRMMathInlineAttachment.m +4 -4
  171. package/ios/attachments/ThematicBreakAttachment.h +15 -15
  172. package/ios/attachments/ThematicBreakAttachment.m +33 -33
  173. package/ios/internals/ENRMViewFreeMeasurement.h +208 -208
  174. package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -19
  175. package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -43
  176. package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -69
  177. package/ios/internals/EnrichedTiptapTextState.h +24 -24
  178. package/ios/internals/MeasurementCache.h +308 -308
  179. package/ios/internals/ShadowMeasurementUtils.h +145 -145
  180. package/ios/parser/ENRMTextLinkAttributes.h +7 -7
  181. package/ios/parser/ENRMTiptapParser.h +33 -33
  182. package/ios/parser/ENRMTiptapParser.mm +73 -73
  183. package/ios/parser/TiptapASTNode.m +52 -52
  184. package/ios/renderer/AttributedRenderer.h +23 -23
  185. package/ios/renderer/AttributedRenderer.m +229 -229
  186. package/ios/renderer/BaseRenderer.h +33 -33
  187. package/ios/renderer/BaseRenderer.m +46 -46
  188. package/ios/renderer/BaselineShiftTextAttributes.h +10 -10
  189. package/ios/renderer/BaselineShiftTextAttributes.m +26 -26
  190. package/ios/renderer/BlankLineRenderer.h +4 -4
  191. package/ios/renderer/BlankLineRenderer.m +51 -51
  192. package/ios/renderer/BlockquoteRenderer.h +6 -6
  193. package/ios/renderer/BlockquoteRenderer.m +258 -258
  194. package/ios/renderer/CodeBlockRenderer.h +9 -9
  195. package/ios/renderer/CodeBlockRenderer.m +99 -99
  196. package/ios/renderer/CodeRenderer.h +9 -9
  197. package/ios/renderer/CodeRenderer.m +69 -69
  198. package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -25
  199. package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -48
  200. package/ios/renderer/EmphasisRenderer.h +5 -5
  201. package/ios/renderer/EmphasisRenderer.m +99 -99
  202. package/ios/renderer/HeadingRenderer.h +6 -6
  203. package/ios/renderer/HeadingRenderer.m +102 -102
  204. package/ios/renderer/HighlightRenderer.h +7 -7
  205. package/ios/renderer/HighlightRenderer.m +33 -33
  206. package/ios/renderer/LinkRenderer.h +6 -6
  207. package/ios/renderer/LinkRenderer.m +157 -157
  208. package/ios/renderer/ListItemRenderer.h +24 -24
  209. package/ios/renderer/ListItemRenderer.m +283 -283
  210. package/ios/renderer/ListRenderer.h +13 -13
  211. package/ios/renderer/ListRenderer.m +126 -126
  212. package/ios/renderer/MentionRenderer.h +9 -9
  213. package/ios/renderer/MentionRenderer.m +96 -96
  214. package/ios/renderer/NodeRenderer.h +8 -8
  215. package/ios/renderer/ParagraphRenderer.h +6 -6
  216. package/ios/renderer/ParagraphRenderer.m +85 -85
  217. package/ios/renderer/RenderContext.h +145 -145
  218. package/ios/renderer/RenderContext.m +402 -402
  219. package/ios/renderer/RendererFactory.h +12 -12
  220. package/ios/renderer/StrikethroughRenderer.h +5 -5
  221. package/ios/renderer/StrikethroughRenderer.m +30 -30
  222. package/ios/renderer/StrongRenderer.h +5 -5
  223. package/ios/renderer/StrongRenderer.m +87 -87
  224. package/ios/renderer/TextRenderer.h +6 -6
  225. package/ios/renderer/TextRenderer.m +18 -18
  226. package/ios/renderer/ThematicBreakRenderer.h +4 -4
  227. package/ios/renderer/ThematicBreakRenderer.m +41 -41
  228. package/ios/renderer/UnderlineRenderer.h +4 -4
  229. package/ios/renderer/UnderlineRenderer.m +41 -41
  230. package/ios/styles/ENRMFontSlot.h +12 -12
  231. package/ios/styles/ENRMFontSlot.m +24 -24
  232. package/ios/styles/StyleConfig.h +488 -488
  233. package/ios/styles/StyleConfig.mm +2820 -2820
  234. package/ios/utils/AccessibilityInfo.h +35 -35
  235. package/ios/utils/AccessibilityInfo.m +24 -24
  236. package/ios/utils/BlockquoteBorder.h +26 -26
  237. package/ios/utils/BlockquoteBorder.m +294 -294
  238. package/ios/utils/CodeBackground.h +19 -19
  239. package/ios/utils/CodeBackground.m +196 -196
  240. package/ios/utils/CodeBlockBackground.h +17 -17
  241. package/ios/utils/CodeBlockBackground.m +102 -102
  242. package/ios/utils/ContextMenuUtils.h +76 -76
  243. package/ios/utils/ContextMenuUtils.mm +75 -75
  244. package/ios/utils/ENRMAccessibilityLabels.h +40 -40
  245. package/ios/utils/ENRMAccessibilityLabels.m +5 -5
  246. package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -24
  247. package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -37
  248. package/ios/utils/ENRMAtomicSize.h +65 -65
  249. package/ios/utils/ENRMCodeBlockContent.h +37 -37
  250. package/ios/utils/ENRMCodeBlockContent.mm +89 -89
  251. package/ios/utils/ENRMFeatureFlags.h +25 -25
  252. package/ios/utils/ENRMImageDownloader.h +30 -30
  253. package/ios/utils/ENRMImageDownloader.m +168 -168
  254. package/ios/utils/ENRMLatexErrorCoordinator.h +27 -27
  255. package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -75
  256. package/ios/utils/ENRMLatexErrorReporting.h +13 -13
  257. package/ios/utils/ENRMLinkContextMenus.h +93 -93
  258. package/ios/utils/ENRMLinkContextMenus.m +178 -178
  259. package/ios/utils/ENRMLinkRegexConfig.h +30 -30
  260. package/ios/utils/ENRMLinkRegexConfig.mm +104 -104
  261. package/ios/utils/ENRMLinkRegexProps.h +23 -23
  262. package/ios/utils/ENRMLocalImageLoader.h +35 -35
  263. package/ios/utils/ENRMLocalImageLoader.m +111 -111
  264. package/ios/utils/ENRMMenuAction.h +27 -27
  265. package/ios/utils/ENRMMenuAction.m +33 -33
  266. package/ios/utils/ENRMSpoilerOverlayManager.h +15 -15
  267. package/ios/utils/ENRMSpoilerOverlayManager.m +19 -19
  268. package/ios/utils/ENRMSpoilerTapUtils.h +6 -6
  269. package/ios/utils/ENRMSpoilerTapUtils.m +11 -11
  270. package/ios/utils/ENRMTailFadeInAnimator.h +16 -16
  271. package/ios/utils/ENRMTailFadeInAnimator.m +161 -161
  272. package/ios/utils/ENRMTextHitTest.h +23 -23
  273. package/ios/utils/ENRMTextHitTest.m +20 -20
  274. package/ios/utils/ENRMTextInteractionUtils.h +20 -20
  275. package/ios/utils/ENRMTextInteractionUtils.m +15 -15
  276. package/ios/utils/ENRMTextRenderer.h +38 -38
  277. package/ios/utils/ENRMTextRenderer.m +54 -54
  278. package/ios/utils/ENRMTextViewSetup.h +132 -132
  279. package/ios/utils/ENRMUIKit.h +429 -429
  280. package/ios/utils/EditMenuUtils.h +105 -105
  281. package/ios/utils/EditMenuUtils.m +143 -143
  282. package/ios/utils/FontScaleObserver.h +10 -10
  283. package/ios/utils/FontScaleObserver.m +55 -55
  284. package/ios/utils/FontUtils.h +31 -31
  285. package/ios/utils/FontUtils.m +63 -63
  286. package/ios/utils/HTMLGenerator.h +25 -25
  287. package/ios/utils/HTMLGenerator.m +1041 -1041
  288. package/ios/utils/HeightUpdateUtils.h +25 -25
  289. package/ios/utils/ImageRequestHeaderUtils.h +35 -35
  290. package/ios/utils/LastElementUtils.h +97 -97
  291. package/ios/utils/LinkContextMenuUtils.h +55 -55
  292. package/ios/utils/LinkPillContentUtils.h +46 -46
  293. package/ios/utils/ListMarkerDrawer.h +15 -15
  294. package/ios/utils/ListMarkerDrawer.m +239 -239
  295. package/ios/utils/MentionBackground.h +26 -26
  296. package/ios/utils/MentionBackground.m +86 -86
  297. package/ios/utils/ParagraphStyleUtils.h +87 -87
  298. package/ios/utils/ParagraphStyleUtils.m +484 -484
  299. package/ios/utils/PasteboardUtils.h +48 -48
  300. package/ios/utils/PasteboardUtils.m +166 -166
  301. package/ios/utils/RTFExportUtils.h +24 -24
  302. package/ios/utils/RTFExportUtils.m +299 -299
  303. package/ios/utils/SelectionColorUtils.h +14 -14
  304. package/ios/utils/StylePropsUtils.h +1340 -1340
  305. package/ios/utils/TaskListTapUtils.h +6 -6
  306. package/ios/utils/TaskListTapUtils.m +14 -14
  307. package/ios/utils/TextViewLayoutManager.h +14 -14
  308. package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -43
  309. package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -432
  310. package/ios/utils/TiptapExtractor.h +17 -17
  311. package/ios/utils/TiptapExtractor.m +367 -367
  312. package/ios/views/ENRMTiptapTextView.h +25 -25
  313. package/ios/views/ENRMTiptapTextView.m +247 -247
  314. package/package.json +65 -70
  315. package/react-native.config.js +10 -10
  316. package/src/admonitionDefaults.ts +58 -58
  317. package/src/darkModeTextStyle.ts +99 -99
  318. package/src/headingDefaults.ts +21 -21
  319. package/src/index.tsx +4 -4
  320. package/src/linkVariantUtils.ts +120 -120
  321. package/src/normalizeTiptapStyle.ts +397 -397
  322. package/src/rn-globals.d.ts +1 -1
  323. package/src/styleUtils.ts +107 -107
  324. package/src/types/TiptapStyle.ts +532 -532
  325. package/src/types/TiptapStyleInternal.ts +299 -299
  326. package/src/types/linkPill.ts +9 -9
  327. package/src/utils/isDev.ts +4 -4
@@ -1,367 +1,367 @@
1
- #import "TiptapExtractor.h"
2
- #import "BaselineShiftTextAttributes.h"
3
- #import "BlockquoteBorder.h"
4
- #import "CodeBackground.h"
5
- #import "ENRMFeatureFlags.h"
6
- #import "ENRMImageAttachment.h"
7
- #import "ENRMLinkPillText.h"
8
- #import "ENRMTextLinkAttributes.h"
9
- #import "ENRMUIKit.h"
10
- #import "HighlightRenderer.h"
11
- #include <TargetConditionals.h>
12
- #if ENRICHED_TIPTAP_MATH
13
- #import "ENRMMathInlineAttachment.h"
14
- #endif
15
- #import "LastElementUtils.h"
16
- #import "ListItemRenderer.h"
17
- #import "RuntimeKeys.h"
18
- #import "ThematicBreakAttachment.h"
19
-
20
- #pragma mark - Extraction Context
21
-
22
- typedef struct {
23
- NSInteger blockquoteDepth; // -1 = not in blockquote
24
- NSInteger listDepth; // -1 = not in list
25
- BOOL needsBlankLine;
26
- } ExtractionState;
27
-
28
- #pragma mark - Helper Functions
29
-
30
- static void ensureBlankLine(NSMutableString *result)
31
- {
32
- if (result.length == 0)
33
- return;
34
- if ([result hasSuffix:@"\n\n"])
35
- return;
36
-
37
- [result appendString:[result hasSuffix:@"\n"] ? @"\n" : @"\n\n"];
38
- }
39
-
40
- static BOOL isAtLineStart(NSMutableString *result)
41
- {
42
- return result.length == 0 || [result hasSuffix:@"\n"];
43
- }
44
-
45
- /// Depth 0 = "> ", Depth 1 = "> > ", etc.
46
- static NSString *buildBlockquotePrefix(NSInteger depth)
47
- {
48
- NSMutableString *prefix = [NSMutableString string];
49
- for (NSInteger i = 0; i <= depth; i++) {
50
- [prefix appendString:@"> "];
51
- }
52
- return prefix;
53
- }
54
-
55
- static NSString *buildListPrefix(NSInteger depth, BOOL isOrdered, NSInteger itemNumber)
56
- {
57
- NSString *indent = [@"" stringByPaddingToLength:(depth * 2) withString:@" " startingAtIndex:0];
58
- NSString *marker = isOrdered ? [NSString stringWithFormat:@"%ld.", (long)itemNumber] : @"-";
59
- return [NSString stringWithFormat:@"%@%@ ", indent, marker];
60
- }
61
-
62
- static NSString *buildHeadingPrefix(NSInteger level)
63
- {
64
- return [NSString stringWithFormat:@"%@ ", [@"" stringByPaddingToLength:level withString:@"#" startingAtIndex:0]];
65
- }
66
-
67
- static void extractFontTraits(NSDictionary *attrs, BOOL *isBold, BOOL *isItalic, BOOL *isMonospace)
68
- {
69
- UIFont *font = attrs[NSFontAttributeName];
70
- *isBold = NO;
71
- *isItalic = NO;
72
- *isMonospace = NO;
73
-
74
- if (font) {
75
- UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
76
- *isBold = (traits & UIFontDescriptorTraitBold) != 0;
77
- *isItalic = (traits & UIFontDescriptorTraitItalic) != 0;
78
- #if !TARGET_OS_OSX
79
- *isMonospace = (traits & UIFontDescriptorTraitMonoSpace) != 0;
80
- #else
81
- *isMonospace = (traits & NSFontDescriptorTraitMonoSpace) != 0;
82
- #endif
83
- }
84
- }
85
-
86
- static NSString *applyInlineFormatting(NSString *text, BOOL isBold, BOOL isItalic, BOOL isMonospace,
87
- BOOL isStrikethrough, BOOL isUnderline, BOOL isSuperscript, BOOL isSubscript,
88
- BOOL isHighlight, NSString *linkURL)
89
- {
90
- NSMutableString *result = [NSMutableString stringWithString:text];
91
-
92
- // Innermost first
93
- if (isMonospace && !linkURL) {
94
- result = [NSMutableString stringWithFormat:@"`%@`", result];
95
- }
96
- if (isStrikethrough) {
97
- result = [NSMutableString stringWithFormat:@"~~%@~~", result];
98
- }
99
- if (isSubscript) {
100
- result = [NSMutableString stringWithFormat:@"~%@~", result];
101
- }
102
- if (isSuperscript) {
103
- result = [NSMutableString stringWithFormat:@"^%@^", result];
104
- }
105
- if (isUnderline && !linkURL) {
106
- result = [NSMutableString stringWithFormat:@"<u>%@</u>", result];
107
- }
108
- if (isItalic) {
109
- result = [NSMutableString stringWithFormat:@"*%@*", result];
110
- }
111
- if (isBold) {
112
- result = [NSMutableString stringWithFormat:@"**%@**", result];
113
- }
114
- if (linkURL) {
115
- result = [NSMutableString stringWithFormat:@"[%@](%@)", result, linkURL];
116
- }
117
- if (isHighlight) {
118
- result = [NSMutableString stringWithFormat:@"==%@==", result];
119
- }
120
-
121
- return result;
122
- }
123
-
124
- #pragma mark - Main Extraction Function
125
-
126
- NSString *_Nullable extractTiptapFromAttributedString(NSAttributedString *attributedText, NSRange range)
127
- {
128
- if (!attributedText || range.length == 0 || range.location >= attributedText.length) {
129
- return nil;
130
- }
131
-
132
- range.length = MIN(range.length, attributedText.length - range.location);
133
- attributedText = ENRMAttributedStringByExpandingLinkPills(attributedText, &range);
134
-
135
- NSMutableString *result = [NSMutableString string];
136
-
137
- // Headings may span multiple attribute runs
138
- __block NSString *currentHeadingType = nil;
139
- __block NSMutableString *headingContent = nil;
140
- __block ExtractionState state = {.blockquoteDepth = -1, .listDepth = -1, .needsBlankLine = NO};
141
-
142
- void (^flushHeading)(void) = ^{
143
- if (!currentHeadingType || headingContent.length == 0)
144
- return;
145
-
146
- ensureBlankLine(result);
147
- NSInteger level = [[currentHeadingType substringFromIndex:8] integerValue];
148
- [result appendFormat:@"%@%@\n", buildHeadingPrefix(level), headingContent];
149
-
150
- currentHeadingType = nil;
151
- headingContent = nil;
152
- state.needsBlankLine = YES;
153
- };
154
-
155
- [attributedText
156
- enumerateAttributesInRange:range
157
- options:0
158
- usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange attrRange, BOOL *stop) {
159
- NSString *text = [[attributedText attributedSubstringFromRange:attrRange] string];
160
- if (text.length == 0)
161
- return;
162
-
163
- // Images and Thematic Breaks
164
- NSTextAttachment *attachment = attrs[NSAttachmentAttributeName];
165
- if ([attachment isKindOfClass:[ENRMImageAttachment class]]) {
166
- ENRMImageAttachment *img = (ENRMImageAttachment *)attachment;
167
- if (!img.imageURL)
168
- return;
169
-
170
- if (img.isInline) {
171
- [result appendFormat:@"![image](%@)", img.imageURL];
172
- } else {
173
- ensureBlankLine(result);
174
- [result appendFormat:@"![image](%@)\n", img.imageURL];
175
- state.needsBlankLine = YES;
176
- state.blockquoteDepth = -1;
177
- state.listDepth = -1;
178
- }
179
- return;
180
- }
181
-
182
- if ([attachment isKindOfClass:[ThematicBreakAttachment class]]) {
183
- ensureBlankLine(result);
184
- [result appendString:@"---\n"];
185
- state.needsBlankLine = YES;
186
- state.blockquoteDepth = -1;
187
- state.listDepth = -1;
188
- return;
189
- }
190
-
191
- #if ENRICHED_TIPTAP_MATH
192
- if ([attachment isKindOfClass:[ENRMMathInlineAttachment class]]) {
193
- ENRMMathInlineAttachment *math = (ENRMMathInlineAttachment *)attachment;
194
- if (math.latex.length > 0) {
195
- [result appendFormat:@"$%@$", math.latex];
196
- }
197
- return;
198
- }
199
- #endif
200
-
201
- if ([text isEqualToString:@"\uFFFC"])
202
- return;
203
-
204
- // Newlines
205
- if ([text isEqualToString:@"\n"] || [text isEqualToString:@"\n\n"]) {
206
- NSNumber *bqDepth = attrs[BlockquoteDepthAttributeName];
207
- NSNumber *listDepth = attrs[ListDepthAttribute];
208
- BOOL inBlockquote = (bqDepth != nil);
209
- BOOL inList = (listDepth != nil);
210
-
211
- if (!inBlockquote && state.blockquoteDepth >= 0) {
212
- ensureBlankLine(result);
213
- state.blockquoteDepth = -1;
214
- return;
215
- }
216
-
217
- if (!inList && state.listDepth >= 0) {
218
- ensureBlankLine(result);
219
- state.listDepth = -1;
220
- return;
221
- }
222
-
223
- if (inBlockquote || inList) {
224
- if (![result hasSuffix:@"\n"]) {
225
- [result appendString:@"\n"];
226
- }
227
- return;
228
- }
229
-
230
- ensureBlankLine(result);
231
- return;
232
- }
233
-
234
- // Headings
235
- NSString *tiptapType = attrs[TiptapTypeAttributeName];
236
-
237
- if (tiptapType && [tiptapType hasPrefix:@"heading-"]) {
238
- if (![tiptapType isEqualToString:currentHeadingType]) {
239
- flushHeading();
240
- currentHeadingType = tiptapType;
241
- headingContent = [NSMutableString string];
242
- }
243
- [headingContent
244
- appendString:[text stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]];
245
- return;
246
- } else if (currentHeadingType) {
247
- flushHeading();
248
- }
249
-
250
- // Code blocks
251
- NSNumber *isCodeBlock = attrs[CodeBlockAttributeName];
252
- if ([isCodeBlock boolValue]) {
253
- if (state.needsBlankLine) {
254
- ensureBlankLine(result);
255
- state.needsBlankLine = NO;
256
- }
257
-
258
- BOOL needsFence = (result.length == 0) || [result hasSuffix:@"\n\n"];
259
- if (needsFence) {
260
- [result appendString:@"```\n"];
261
- }
262
-
263
- [result appendString:text];
264
-
265
- if ([text hasSuffix:@"\n"]) {
266
- [result appendString:@"```\n"];
267
- state.needsBlankLine = YES;
268
- }
269
- return;
270
- }
271
-
272
- // Blockquotes
273
- NSNumber *bqDepthNum = attrs[BlockquoteDepthAttributeName];
274
- NSInteger currentBqDepth = bqDepthNum ? [bqDepthNum integerValue] : -1;
275
- NSString *blockquotePrefix = nil;
276
-
277
- if (currentBqDepth >= 0) {
278
- blockquotePrefix = buildBlockquotePrefix(currentBqDepth);
279
- state.blockquoteDepth = currentBqDepth;
280
- } else if (state.blockquoteDepth >= 0) {
281
- ensureBlankLine(result);
282
- state.blockquoteDepth = -1;
283
- }
284
-
285
- // Lists
286
- NSNumber *listDepthNum = attrs[ListDepthAttribute];
287
- NSNumber *listTypeNum = attrs[ListTypeAttribute];
288
- NSNumber *listItemNum = attrs[ListItemNumberAttribute];
289
- BOOL isTaskItem = [attrs[TaskItemAttribute] boolValue];
290
- BOOL isTaskChecked = [attrs[TaskCheckedAttribute] boolValue];
291
- NSInteger currentListDepth = listDepthNum ? [listDepthNum integerValue] : -1;
292
-
293
- if (currentListDepth >= 0) {
294
- state.listDepth = currentListDepth;
295
- } else if (state.listDepth >= 0) {
296
- ensureBlankLine(result);
297
- state.listDepth = -1;
298
- }
299
-
300
- // Inline formatting
301
- BOOL isBold, isItalic, isMonospace;
302
- BOOL recognizedLink = [attrs[ENRMRecognizedLinkAttributeName] boolValue];
303
- extractFontTraits(attrs, &isBold, &isItalic, &isMonospace);
304
- if (recognizedLink)
305
- isMonospace = [attrs[CodeAttributeName] boolValue];
306
-
307
- NSNumber *strikethroughStyle = attrs[NSStrikethroughStyleAttributeName];
308
- BOOL isStrikethrough = (strikethroughStyle != nil && [strikethroughStyle integerValue] != 0);
309
- NSNumber *underlineStyle = recognizedLink
310
- ? attrs[ENRMRecognizedLinkOriginalUnderlineAttributeName]
311
- : attrs[NSUnderlineStyleAttributeName];
312
- BOOL isUnderline = (underlineStyle != nil && [underlineStyle integerValue] != 0);
313
- // Line-height alignment also shifts baselines. Only semantic script spans
314
- // should produce superscript/subscript Tiptap on the clipboard.
315
- NSString *script = attrs[ENRMScriptAttributeName];
316
- BOOL isSuperscript = [script isEqualToString:ENRMScriptValueSuperscript];
317
- BOOL isSubscript = [script isEqualToString:ENRMScriptValueSubscript];
318
-
319
- BOOL isHighlight = [attrs[HighlightAttributeName] boolValue];
320
- NSString *linkURL = recognizedLink ? nil : attrs[NSLinkAttributeName];
321
- NSString *segment =
322
- applyInlineFormatting(text, isBold, isItalic, isMonospace, isStrikethrough, isUnderline,
323
- isSuperscript, isSubscript, isHighlight, linkURL);
324
-
325
- // Add block prefixes at line start
326
- if (isAtLineStart(result)) {
327
- NSMutableString *prefixedSegment = [NSMutableString string];
328
-
329
- if (listDepthNum && ![text hasPrefix:@"\n"]) {
330
- if (isTaskItem) {
331
- NSString *indent = [@"" stringByPaddingToLength:(currentListDepth * 2)
332
- withString:@" "
333
- startingAtIndex:0];
334
- NSString *checkbox = isTaskChecked ? @"[x]" : @"[ ]";
335
- [prefixedSegment appendFormat:@"%@- %@ ", indent, checkbox];
336
- } else {
337
- BOOL isOrdered = ([listTypeNum integerValue] == 1);
338
- NSInteger itemNumber = listItemNum ? [listItemNum integerValue] : 1;
339
- [prefixedSegment appendString:buildListPrefix(currentListDepth, isOrdered, itemNumber)];
340
- }
341
- }
342
-
343
- if (blockquotePrefix) {
344
- [prefixedSegment insertString:blockquotePrefix atIndex:0];
345
- }
346
-
347
- [prefixedSegment appendString:segment];
348
- segment = prefixedSegment;
349
- }
350
-
351
- if (state.needsBlankLine && result.length > 0) {
352
- ensureBlankLine(result);
353
- state.needsBlankLine = NO;
354
- }
355
-
356
- [result appendString:segment];
357
- }];
358
-
359
- // Flush remaining heading
360
- if (currentHeadingType && headingContent.length > 0) {
361
- ensureBlankLine(result);
362
- NSInteger level = [[currentHeadingType substringFromIndex:8] integerValue];
363
- [result appendFormat:@"%@%@\n", buildHeadingPrefix(level), headingContent];
364
- }
365
-
366
- return result.length > 0 ? result : nil;
367
- }
1
+ #import "TiptapExtractor.h"
2
+ #import "BaselineShiftTextAttributes.h"
3
+ #import "BlockquoteBorder.h"
4
+ #import "CodeBackground.h"
5
+ #import "ENRMFeatureFlags.h"
6
+ #import "ENRMImageAttachment.h"
7
+ #import "ENRMLinkPillText.h"
8
+ #import "ENRMTextLinkAttributes.h"
9
+ #import "ENRMUIKit.h"
10
+ #import "HighlightRenderer.h"
11
+ #include <TargetConditionals.h>
12
+ #if ENRICHED_TIPTAP_MATH
13
+ #import "ENRMMathInlineAttachment.h"
14
+ #endif
15
+ #import "LastElementUtils.h"
16
+ #import "ListItemRenderer.h"
17
+ #import "RuntimeKeys.h"
18
+ #import "ThematicBreakAttachment.h"
19
+
20
+ #pragma mark - Extraction Context
21
+
22
+ typedef struct {
23
+ NSInteger blockquoteDepth; // -1 = not in blockquote
24
+ NSInteger listDepth; // -1 = not in list
25
+ BOOL needsBlankLine;
26
+ } ExtractionState;
27
+
28
+ #pragma mark - Helper Functions
29
+
30
+ static void ensureBlankLine(NSMutableString *result)
31
+ {
32
+ if (result.length == 0)
33
+ return;
34
+ if ([result hasSuffix:@"\n\n"])
35
+ return;
36
+
37
+ [result appendString:[result hasSuffix:@"\n"] ? @"\n" : @"\n\n"];
38
+ }
39
+
40
+ static BOOL isAtLineStart(NSMutableString *result)
41
+ {
42
+ return result.length == 0 || [result hasSuffix:@"\n"];
43
+ }
44
+
45
+ /// Depth 0 = "> ", Depth 1 = "> > ", etc.
46
+ static NSString *buildBlockquotePrefix(NSInteger depth)
47
+ {
48
+ NSMutableString *prefix = [NSMutableString string];
49
+ for (NSInteger i = 0; i <= depth; i++) {
50
+ [prefix appendString:@"> "];
51
+ }
52
+ return prefix;
53
+ }
54
+
55
+ static NSString *buildListPrefix(NSInteger depth, BOOL isOrdered, NSInteger itemNumber)
56
+ {
57
+ NSString *indent = [@"" stringByPaddingToLength:(depth * 2) withString:@" " startingAtIndex:0];
58
+ NSString *marker = isOrdered ? [NSString stringWithFormat:@"%ld.", (long)itemNumber] : @"-";
59
+ return [NSString stringWithFormat:@"%@%@ ", indent, marker];
60
+ }
61
+
62
+ static NSString *buildHeadingPrefix(NSInteger level)
63
+ {
64
+ return [NSString stringWithFormat:@"%@ ", [@"" stringByPaddingToLength:level withString:@"#" startingAtIndex:0]];
65
+ }
66
+
67
+ static void extractFontTraits(NSDictionary *attrs, BOOL *isBold, BOOL *isItalic, BOOL *isMonospace)
68
+ {
69
+ UIFont *font = attrs[NSFontAttributeName];
70
+ *isBold = NO;
71
+ *isItalic = NO;
72
+ *isMonospace = NO;
73
+
74
+ if (font) {
75
+ UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
76
+ *isBold = (traits & UIFontDescriptorTraitBold) != 0;
77
+ *isItalic = (traits & UIFontDescriptorTraitItalic) != 0;
78
+ #if !TARGET_OS_OSX
79
+ *isMonospace = (traits & UIFontDescriptorTraitMonoSpace) != 0;
80
+ #else
81
+ *isMonospace = (traits & NSFontDescriptorTraitMonoSpace) != 0;
82
+ #endif
83
+ }
84
+ }
85
+
86
+ static NSString *applyInlineFormatting(NSString *text, BOOL isBold, BOOL isItalic, BOOL isMonospace,
87
+ BOOL isStrikethrough, BOOL isUnderline, BOOL isSuperscript, BOOL isSubscript,
88
+ BOOL isHighlight, NSString *linkURL)
89
+ {
90
+ NSMutableString *result = [NSMutableString stringWithString:text];
91
+
92
+ // Innermost first
93
+ if (isMonospace && !linkURL) {
94
+ result = [NSMutableString stringWithFormat:@"`%@`", result];
95
+ }
96
+ if (isStrikethrough) {
97
+ result = [NSMutableString stringWithFormat:@"~~%@~~", result];
98
+ }
99
+ if (isSubscript) {
100
+ result = [NSMutableString stringWithFormat:@"~%@~", result];
101
+ }
102
+ if (isSuperscript) {
103
+ result = [NSMutableString stringWithFormat:@"^%@^", result];
104
+ }
105
+ if (isUnderline && !linkURL) {
106
+ result = [NSMutableString stringWithFormat:@"<u>%@</u>", result];
107
+ }
108
+ if (isItalic) {
109
+ result = [NSMutableString stringWithFormat:@"*%@*", result];
110
+ }
111
+ if (isBold) {
112
+ result = [NSMutableString stringWithFormat:@"**%@**", result];
113
+ }
114
+ if (linkURL) {
115
+ result = [NSMutableString stringWithFormat:@"[%@](%@)", result, linkURL];
116
+ }
117
+ if (isHighlight) {
118
+ result = [NSMutableString stringWithFormat:@"==%@==", result];
119
+ }
120
+
121
+ return result;
122
+ }
123
+
124
+ #pragma mark - Main Extraction Function
125
+
126
+ NSString *_Nullable extractTiptapFromAttributedString(NSAttributedString *attributedText, NSRange range)
127
+ {
128
+ if (!attributedText || range.length == 0 || range.location >= attributedText.length) {
129
+ return nil;
130
+ }
131
+
132
+ range.length = MIN(range.length, attributedText.length - range.location);
133
+ attributedText = ENRMAttributedStringByExpandingLinkPills(attributedText, &range);
134
+
135
+ NSMutableString *result = [NSMutableString string];
136
+
137
+ // Headings may span multiple attribute runs
138
+ __block NSString *currentHeadingType = nil;
139
+ __block NSMutableString *headingContent = nil;
140
+ __block ExtractionState state = {.blockquoteDepth = -1, .listDepth = -1, .needsBlankLine = NO};
141
+
142
+ void (^flushHeading)(void) = ^{
143
+ if (!currentHeadingType || headingContent.length == 0)
144
+ return;
145
+
146
+ ensureBlankLine(result);
147
+ NSInteger level = [[currentHeadingType substringFromIndex:8] integerValue];
148
+ [result appendFormat:@"%@%@\n", buildHeadingPrefix(level), headingContent];
149
+
150
+ currentHeadingType = nil;
151
+ headingContent = nil;
152
+ state.needsBlankLine = YES;
153
+ };
154
+
155
+ [attributedText
156
+ enumerateAttributesInRange:range
157
+ options:0
158
+ usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange attrRange, BOOL *stop) {
159
+ NSString *text = [[attributedText attributedSubstringFromRange:attrRange] string];
160
+ if (text.length == 0)
161
+ return;
162
+
163
+ // Images and Thematic Breaks
164
+ NSTextAttachment *attachment = attrs[NSAttachmentAttributeName];
165
+ if ([attachment isKindOfClass:[ENRMImageAttachment class]]) {
166
+ ENRMImageAttachment *img = (ENRMImageAttachment *)attachment;
167
+ if (!img.imageURL)
168
+ return;
169
+
170
+ if (img.isInline) {
171
+ [result appendFormat:@"![image](%@)", img.imageURL];
172
+ } else {
173
+ ensureBlankLine(result);
174
+ [result appendFormat:@"![image](%@)\n", img.imageURL];
175
+ state.needsBlankLine = YES;
176
+ state.blockquoteDepth = -1;
177
+ state.listDepth = -1;
178
+ }
179
+ return;
180
+ }
181
+
182
+ if ([attachment isKindOfClass:[ThematicBreakAttachment class]]) {
183
+ ensureBlankLine(result);
184
+ [result appendString:@"---\n"];
185
+ state.needsBlankLine = YES;
186
+ state.blockquoteDepth = -1;
187
+ state.listDepth = -1;
188
+ return;
189
+ }
190
+
191
+ #if ENRICHED_TIPTAP_MATH
192
+ if ([attachment isKindOfClass:[ENRMMathInlineAttachment class]]) {
193
+ ENRMMathInlineAttachment *math = (ENRMMathInlineAttachment *)attachment;
194
+ if (math.latex.length > 0) {
195
+ [result appendFormat:@"$%@$", math.latex];
196
+ }
197
+ return;
198
+ }
199
+ #endif
200
+
201
+ if ([text isEqualToString:@"\uFFFC"])
202
+ return;
203
+
204
+ // Newlines
205
+ if ([text isEqualToString:@"\n"] || [text isEqualToString:@"\n\n"]) {
206
+ NSNumber *bqDepth = attrs[BlockquoteDepthAttributeName];
207
+ NSNumber *listDepth = attrs[ListDepthAttribute];
208
+ BOOL inBlockquote = (bqDepth != nil);
209
+ BOOL inList = (listDepth != nil);
210
+
211
+ if (!inBlockquote && state.blockquoteDepth >= 0) {
212
+ ensureBlankLine(result);
213
+ state.blockquoteDepth = -1;
214
+ return;
215
+ }
216
+
217
+ if (!inList && state.listDepth >= 0) {
218
+ ensureBlankLine(result);
219
+ state.listDepth = -1;
220
+ return;
221
+ }
222
+
223
+ if (inBlockquote || inList) {
224
+ if (![result hasSuffix:@"\n"]) {
225
+ [result appendString:@"\n"];
226
+ }
227
+ return;
228
+ }
229
+
230
+ ensureBlankLine(result);
231
+ return;
232
+ }
233
+
234
+ // Headings
235
+ NSString *tiptapType = attrs[TiptapTypeAttributeName];
236
+
237
+ if (tiptapType && [tiptapType hasPrefix:@"heading-"]) {
238
+ if (![tiptapType isEqualToString:currentHeadingType]) {
239
+ flushHeading();
240
+ currentHeadingType = tiptapType;
241
+ headingContent = [NSMutableString string];
242
+ }
243
+ [headingContent
244
+ appendString:[text stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]];
245
+ return;
246
+ } else if (currentHeadingType) {
247
+ flushHeading();
248
+ }
249
+
250
+ // Code blocks
251
+ NSNumber *isCodeBlock = attrs[CodeBlockAttributeName];
252
+ if ([isCodeBlock boolValue]) {
253
+ if (state.needsBlankLine) {
254
+ ensureBlankLine(result);
255
+ state.needsBlankLine = NO;
256
+ }
257
+
258
+ BOOL needsFence = (result.length == 0) || [result hasSuffix:@"\n\n"];
259
+ if (needsFence) {
260
+ [result appendString:@"```\n"];
261
+ }
262
+
263
+ [result appendString:text];
264
+
265
+ if ([text hasSuffix:@"\n"]) {
266
+ [result appendString:@"```\n"];
267
+ state.needsBlankLine = YES;
268
+ }
269
+ return;
270
+ }
271
+
272
+ // Blockquotes
273
+ NSNumber *bqDepthNum = attrs[BlockquoteDepthAttributeName];
274
+ NSInteger currentBqDepth = bqDepthNum ? [bqDepthNum integerValue] : -1;
275
+ NSString *blockquotePrefix = nil;
276
+
277
+ if (currentBqDepth >= 0) {
278
+ blockquotePrefix = buildBlockquotePrefix(currentBqDepth);
279
+ state.blockquoteDepth = currentBqDepth;
280
+ } else if (state.blockquoteDepth >= 0) {
281
+ ensureBlankLine(result);
282
+ state.blockquoteDepth = -1;
283
+ }
284
+
285
+ // Lists
286
+ NSNumber *listDepthNum = attrs[ListDepthAttribute];
287
+ NSNumber *listTypeNum = attrs[ListTypeAttribute];
288
+ NSNumber *listItemNum = attrs[ListItemNumberAttribute];
289
+ BOOL isTaskItem = [attrs[TaskItemAttribute] boolValue];
290
+ BOOL isTaskChecked = [attrs[TaskCheckedAttribute] boolValue];
291
+ NSInteger currentListDepth = listDepthNum ? [listDepthNum integerValue] : -1;
292
+
293
+ if (currentListDepth >= 0) {
294
+ state.listDepth = currentListDepth;
295
+ } else if (state.listDepth >= 0) {
296
+ ensureBlankLine(result);
297
+ state.listDepth = -1;
298
+ }
299
+
300
+ // Inline formatting
301
+ BOOL isBold, isItalic, isMonospace;
302
+ BOOL recognizedLink = [attrs[ENRMRecognizedLinkAttributeName] boolValue];
303
+ extractFontTraits(attrs, &isBold, &isItalic, &isMonospace);
304
+ if (recognizedLink)
305
+ isMonospace = [attrs[CodeAttributeName] boolValue];
306
+
307
+ NSNumber *strikethroughStyle = attrs[NSStrikethroughStyleAttributeName];
308
+ BOOL isStrikethrough = (strikethroughStyle != nil && [strikethroughStyle integerValue] != 0);
309
+ NSNumber *underlineStyle = recognizedLink
310
+ ? attrs[ENRMRecognizedLinkOriginalUnderlineAttributeName]
311
+ : attrs[NSUnderlineStyleAttributeName];
312
+ BOOL isUnderline = (underlineStyle != nil && [underlineStyle integerValue] != 0);
313
+ // Line-height alignment also shifts baselines. Only semantic script spans
314
+ // should produce superscript/subscript Tiptap on the clipboard.
315
+ NSString *script = attrs[ENRMScriptAttributeName];
316
+ BOOL isSuperscript = [script isEqualToString:ENRMScriptValueSuperscript];
317
+ BOOL isSubscript = [script isEqualToString:ENRMScriptValueSubscript];
318
+
319
+ BOOL isHighlight = [attrs[HighlightAttributeName] boolValue];
320
+ NSString *linkURL = recognizedLink ? nil : attrs[NSLinkAttributeName];
321
+ NSString *segment =
322
+ applyInlineFormatting(text, isBold, isItalic, isMonospace, isStrikethrough, isUnderline,
323
+ isSuperscript, isSubscript, isHighlight, linkURL);
324
+
325
+ // Add block prefixes at line start
326
+ if (isAtLineStart(result)) {
327
+ NSMutableString *prefixedSegment = [NSMutableString string];
328
+
329
+ if (listDepthNum && ![text hasPrefix:@"\n"]) {
330
+ if (isTaskItem) {
331
+ NSString *indent = [@"" stringByPaddingToLength:(currentListDepth * 2)
332
+ withString:@" "
333
+ startingAtIndex:0];
334
+ NSString *checkbox = isTaskChecked ? @"[x]" : @"[ ]";
335
+ [prefixedSegment appendFormat:@"%@- %@ ", indent, checkbox];
336
+ } else {
337
+ BOOL isOrdered = ([listTypeNum integerValue] == 1);
338
+ NSInteger itemNumber = listItemNum ? [listItemNum integerValue] : 1;
339
+ [prefixedSegment appendString:buildListPrefix(currentListDepth, isOrdered, itemNumber)];
340
+ }
341
+ }
342
+
343
+ if (blockquotePrefix) {
344
+ [prefixedSegment insertString:blockquotePrefix atIndex:0];
345
+ }
346
+
347
+ [prefixedSegment appendString:segment];
348
+ segment = prefixedSegment;
349
+ }
350
+
351
+ if (state.needsBlankLine && result.length > 0) {
352
+ ensureBlankLine(result);
353
+ state.needsBlankLine = NO;
354
+ }
355
+
356
+ [result appendString:segment];
357
+ }];
358
+
359
+ // Flush remaining heading
360
+ if (currentHeadingType && headingContent.length > 0) {
361
+ ensureBlankLine(result);
362
+ NSInteger level = [[currentHeadingType substringFromIndex:8] integerValue];
363
+ [result appendFormat:@"%@%@\n", buildHeadingPrefix(level), headingContent];
364
+ }
365
+
366
+ return result.length > 0 ? result : nil;
367
+ }