@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.
- package/LICENSE +20 -20
- package/README.md +140 -140
- package/ReactNativeRenderRichText.podspec +23 -23
- package/android/build.gradle +86 -86
- package/android/consumer-rules.pro +3 -3
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -24
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -13
- package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -644
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -17
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -112
- package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -576
- package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -24
- package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -28
- package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -24
- package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -25
- package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -29
- package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -23
- package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -23
- package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -25
- package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -25
- package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -26
- package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -9
- package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -59
- package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -35
- package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -45
- package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -39
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -243
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -56
- package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -135
- package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -37
- package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -28
- package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -81
- package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -26
- package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -17
- package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -90
- package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -109
- package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -211
- package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -133
- package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -85
- package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -91
- package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -148
- package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -17
- package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -143
- package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -28
- package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -28
- package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -31
- package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -46
- package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -28
- package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -164
- package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -27
- package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -302
- package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -205
- package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -216
- package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -32
- package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -42
- package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -29
- package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -38
- package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -80
- package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -481
- package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -42
- package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -39
- package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -154
- package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -381
- package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -68
- package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -33
- package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -38
- package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -76
- package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -132
- package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -46
- package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -127
- package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -33
- package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -12
- package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -39
- package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -143
- package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -26
- package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -69
- package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -96
- package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -46
- package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -11
- package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -83
- package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -80
- package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -26
- package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -21
- package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -21
- package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -33
- package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -19
- package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -29
- package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -17
- package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -17
- package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -36
- package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -44
- package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -23
- package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -34
- package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -65
- package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -29
- package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -19
- package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -33
- package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -27
- package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -17
- package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -21
- package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -411
- package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -92
- package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -16
- package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -16
- package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -75
- package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -29
- package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -32
- package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -23
- package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -17
- package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -31
- package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -86
- package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -32
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -36
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -42
- package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -27
- package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -10
- package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -9
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -31
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -56
- package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -18
- package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -28
- package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -6
- package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -368
- package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -88
- package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -203
- package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -167
- package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -84
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -1105
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -423
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -9
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -9
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -76
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -61
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -286
- package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -39
- package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -142
- package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -43
- package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -30
- package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -15
- package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -19
- package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -235
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -48
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -119
- package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -217
- package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -101
- package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -40
- package/android/src/main/jni/CMakeLists.txt +52 -52
- package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -30
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -29
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -45
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -21
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -38
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -53
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -9
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -24
- package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -40
- package/cpp/parser/TiptapParser.hpp +13 -13
- package/cpp/vendor/nlohmann/json.hpp +24765 -24765
- package/ios/EnrichedTiptapText.h +27 -27
- package/ios/attachments/ENRMImageAttachment.h +51 -51
- package/ios/attachments/ENRMImageAttachment.m +398 -398
- package/ios/attachments/ENRMLinkPillAttachment.h +47 -47
- package/ios/attachments/ENRMLinkPillAttachment.m +324 -324
- package/ios/attachments/ENRMLinkPillIconCache.h +28 -28
- package/ios/attachments/ENRMLinkPillIconCache.m +181 -181
- package/ios/attachments/ENRMLinkPillText.h +27 -27
- package/ios/attachments/ENRMLinkPillText.m +118 -118
- package/ios/attachments/ENRMMathInlineAttachment.h +7 -7
- package/ios/attachments/ENRMMathInlineAttachment.m +4 -4
- package/ios/attachments/ThematicBreakAttachment.h +15 -15
- package/ios/attachments/ThematicBreakAttachment.m +33 -33
- package/ios/internals/ENRMViewFreeMeasurement.h +208 -208
- package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -19
- package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -43
- package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -69
- package/ios/internals/EnrichedTiptapTextState.h +24 -24
- package/ios/internals/MeasurementCache.h +308 -308
- package/ios/internals/ShadowMeasurementUtils.h +145 -145
- package/ios/parser/ENRMTextLinkAttributes.h +7 -7
- package/ios/parser/ENRMTiptapParser.h +33 -33
- package/ios/parser/ENRMTiptapParser.mm +73 -73
- package/ios/parser/TiptapASTNode.m +52 -52
- package/ios/renderer/AttributedRenderer.h +23 -23
- package/ios/renderer/AttributedRenderer.m +229 -229
- package/ios/renderer/BaseRenderer.h +33 -33
- package/ios/renderer/BaseRenderer.m +46 -46
- package/ios/renderer/BaselineShiftTextAttributes.h +10 -10
- package/ios/renderer/BaselineShiftTextAttributes.m +26 -26
- package/ios/renderer/BlankLineRenderer.h +4 -4
- package/ios/renderer/BlankLineRenderer.m +51 -51
- package/ios/renderer/BlockquoteRenderer.h +6 -6
- package/ios/renderer/BlockquoteRenderer.m +258 -258
- package/ios/renderer/CodeBlockRenderer.h +9 -9
- package/ios/renderer/CodeBlockRenderer.m +99 -99
- package/ios/renderer/CodeRenderer.h +9 -9
- package/ios/renderer/CodeRenderer.m +69 -69
- package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -25
- package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -48
- package/ios/renderer/EmphasisRenderer.h +5 -5
- package/ios/renderer/EmphasisRenderer.m +99 -99
- package/ios/renderer/HeadingRenderer.h +6 -6
- package/ios/renderer/HeadingRenderer.m +102 -102
- package/ios/renderer/HighlightRenderer.h +7 -7
- package/ios/renderer/HighlightRenderer.m +33 -33
- package/ios/renderer/LinkRenderer.h +6 -6
- package/ios/renderer/LinkRenderer.m +157 -157
- package/ios/renderer/ListItemRenderer.h +24 -24
- package/ios/renderer/ListItemRenderer.m +283 -283
- package/ios/renderer/ListRenderer.h +13 -13
- package/ios/renderer/ListRenderer.m +126 -126
- package/ios/renderer/MentionRenderer.h +9 -9
- package/ios/renderer/MentionRenderer.m +96 -96
- package/ios/renderer/NodeRenderer.h +8 -8
- package/ios/renderer/ParagraphRenderer.h +6 -6
- package/ios/renderer/ParagraphRenderer.m +85 -85
- package/ios/renderer/RenderContext.h +145 -145
- package/ios/renderer/RenderContext.m +402 -402
- package/ios/renderer/RendererFactory.h +12 -12
- package/ios/renderer/StrikethroughRenderer.h +5 -5
- package/ios/renderer/StrikethroughRenderer.m +30 -30
- package/ios/renderer/StrongRenderer.h +5 -5
- package/ios/renderer/StrongRenderer.m +87 -87
- package/ios/renderer/TextRenderer.h +6 -6
- package/ios/renderer/TextRenderer.m +18 -18
- package/ios/renderer/ThematicBreakRenderer.h +4 -4
- package/ios/renderer/ThematicBreakRenderer.m +41 -41
- package/ios/renderer/UnderlineRenderer.h +4 -4
- package/ios/renderer/UnderlineRenderer.m +41 -41
- package/ios/styles/ENRMFontSlot.h +12 -12
- package/ios/styles/ENRMFontSlot.m +24 -24
- package/ios/styles/StyleConfig.h +488 -488
- package/ios/styles/StyleConfig.mm +2820 -2820
- package/ios/utils/AccessibilityInfo.h +35 -35
- package/ios/utils/AccessibilityInfo.m +24 -24
- package/ios/utils/BlockquoteBorder.h +26 -26
- package/ios/utils/BlockquoteBorder.m +294 -294
- package/ios/utils/CodeBackground.h +19 -19
- package/ios/utils/CodeBackground.m +196 -196
- package/ios/utils/CodeBlockBackground.h +17 -17
- package/ios/utils/CodeBlockBackground.m +102 -102
- package/ios/utils/ContextMenuUtils.h +76 -76
- package/ios/utils/ContextMenuUtils.mm +75 -75
- package/ios/utils/ENRMAccessibilityLabels.h +40 -40
- package/ios/utils/ENRMAccessibilityLabels.m +5 -5
- package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -24
- package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -37
- package/ios/utils/ENRMAtomicSize.h +65 -65
- package/ios/utils/ENRMCodeBlockContent.h +37 -37
- package/ios/utils/ENRMCodeBlockContent.mm +89 -89
- package/ios/utils/ENRMFeatureFlags.h +25 -25
- package/ios/utils/ENRMImageDownloader.h +30 -30
- package/ios/utils/ENRMImageDownloader.m +168 -168
- package/ios/utils/ENRMLatexErrorCoordinator.h +27 -27
- package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -75
- package/ios/utils/ENRMLatexErrorReporting.h +13 -13
- package/ios/utils/ENRMLinkContextMenus.h +93 -93
- package/ios/utils/ENRMLinkContextMenus.m +178 -178
- package/ios/utils/ENRMLinkRegexConfig.h +30 -30
- package/ios/utils/ENRMLinkRegexConfig.mm +104 -104
- package/ios/utils/ENRMLinkRegexProps.h +23 -23
- package/ios/utils/ENRMLocalImageLoader.h +35 -35
- package/ios/utils/ENRMLocalImageLoader.m +111 -111
- package/ios/utils/ENRMMenuAction.h +27 -27
- package/ios/utils/ENRMMenuAction.m +33 -33
- package/ios/utils/ENRMSpoilerOverlayManager.h +15 -15
- package/ios/utils/ENRMSpoilerOverlayManager.m +19 -19
- package/ios/utils/ENRMSpoilerTapUtils.h +6 -6
- package/ios/utils/ENRMSpoilerTapUtils.m +11 -11
- package/ios/utils/ENRMTailFadeInAnimator.h +16 -16
- package/ios/utils/ENRMTailFadeInAnimator.m +161 -161
- package/ios/utils/ENRMTextHitTest.h +23 -23
- package/ios/utils/ENRMTextHitTest.m +20 -20
- package/ios/utils/ENRMTextInteractionUtils.h +20 -20
- package/ios/utils/ENRMTextInteractionUtils.m +15 -15
- package/ios/utils/ENRMTextRenderer.h +38 -38
- package/ios/utils/ENRMTextRenderer.m +54 -54
- package/ios/utils/ENRMTextViewSetup.h +132 -132
- package/ios/utils/ENRMUIKit.h +429 -429
- package/ios/utils/EditMenuUtils.h +105 -105
- package/ios/utils/EditMenuUtils.m +143 -143
- package/ios/utils/FontScaleObserver.h +10 -10
- package/ios/utils/FontScaleObserver.m +55 -55
- package/ios/utils/FontUtils.h +31 -31
- package/ios/utils/FontUtils.m +63 -63
- package/ios/utils/HTMLGenerator.h +25 -25
- package/ios/utils/HTMLGenerator.m +1041 -1041
- package/ios/utils/HeightUpdateUtils.h +25 -25
- package/ios/utils/ImageRequestHeaderUtils.h +35 -35
- package/ios/utils/LastElementUtils.h +97 -97
- package/ios/utils/LinkContextMenuUtils.h +55 -55
- package/ios/utils/LinkPillContentUtils.h +46 -46
- package/ios/utils/ListMarkerDrawer.h +15 -15
- package/ios/utils/ListMarkerDrawer.m +239 -239
- package/ios/utils/MentionBackground.h +26 -26
- package/ios/utils/MentionBackground.m +86 -86
- package/ios/utils/ParagraphStyleUtils.h +87 -87
- package/ios/utils/ParagraphStyleUtils.m +484 -484
- package/ios/utils/PasteboardUtils.h +48 -48
- package/ios/utils/PasteboardUtils.m +166 -166
- package/ios/utils/RTFExportUtils.h +24 -24
- package/ios/utils/RTFExportUtils.m +299 -299
- package/ios/utils/SelectionColorUtils.h +14 -14
- package/ios/utils/StylePropsUtils.h +1340 -1340
- package/ios/utils/TaskListTapUtils.h +6 -6
- package/ios/utils/TaskListTapUtils.m +14 -14
- package/ios/utils/TextViewLayoutManager.h +14 -14
- package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -43
- package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -432
- package/ios/utils/TiptapExtractor.h +17 -17
- package/ios/utils/TiptapExtractor.m +367 -367
- package/ios/views/ENRMTiptapTextView.h +25 -25
- package/ios/views/ENRMTiptapTextView.m +247 -247
- package/package.json +65 -70
- package/react-native.config.js +10 -10
- package/src/admonitionDefaults.ts +58 -58
- package/src/darkModeTextStyle.ts +99 -99
- package/src/headingDefaults.ts +21 -21
- package/src/index.tsx +4 -4
- package/src/linkVariantUtils.ts +120 -120
- package/src/normalizeTiptapStyle.ts +397 -397
- package/src/rn-globals.d.ts +1 -1
- package/src/styleUtils.ts +107 -107
- package/src/types/TiptapStyle.ts +532 -532
- package/src/types/TiptapStyleInternal.ts +299 -299
- package/src/types/linkPill.ts +9 -9
- 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:@"", img.imageURL];
|
|
172
|
-
} else {
|
|
173
|
-
ensureBlankLine(result);
|
|
174
|
-
[result appendFormat:@"\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:@"", img.imageURL];
|
|
172
|
+
} else {
|
|
173
|
+
ensureBlankLine(result);
|
|
174
|
+
[result appendFormat:@"\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
|
+
}
|