@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +140 -3
- package/ReactNativeRenderRichText.podspec +23 -0
- package/android/build.gradle +86 -0
- package/android/consumer-rules.pro +3 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/jni-adapter.cpp +191 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapText.kt +673 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextManager.kt +405 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -0
- package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -0
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -0
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -0
- package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -0
- package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -0
- package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -0
- package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -0
- package/android/src/main/java/com/renderrichtext/events/FactoidPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -0
- package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -0
- package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -0
- package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -0
- package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -0
- package/android/src/main/java/com/renderrichtext/parser/TiptapASTNode.kt +69 -0
- package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockStyleContext.kt +181 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -0
- package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -0
- package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -0
- package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/FactoidRenderer.kt +97 -0
- package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -0
- package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -0
- package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -0
- package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -0
- package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -0
- package/android/src/main/java/com/renderrichtext/renderer/NodeRenderer.kt +207 -0
- package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -0
- package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -0
- package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -0
- package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -0
- package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -0
- package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -0
- package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -0
- package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -0
- package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -0
- package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidBackgroundSpan.kt +265 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidPaddingSpan.kt +258 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidSpan.kt +32 -0
- package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -0
- package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -0
- package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -0
- package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -0
- package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -0
- package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -0
- package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -0
- package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -0
- package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -0
- package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -0
- package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -0
- package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -0
- package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -0
- package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -0
- package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -0
- package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -0
- package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -0
- package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -0
- package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -0
- package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -0
- package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -0
- package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -0
- package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -0
- package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -0
- package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -0
- package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -0
- package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -0
- package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -0
- package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -0
- package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -0
- package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -0
- package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -0
- package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -0
- package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -0
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -0
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -0
- package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -0
- package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -0
- package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -0
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -0
- package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -0
- package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -0
- package/android/src/main/java/com/renderrichtext/utils/common/TiptapViewManagerUtils.kt +242 -0
- package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -0
- package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -0
- package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -0
- package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -0
- package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -0
- package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -0
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -0
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -0
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -0
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/LinkEvents.kt +88 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/LinkLongPressMovementMethod.kt +449 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -0
- package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -0
- package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -0
- package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -0
- package/android/src/main/jni/CMakeLists.txt +52 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -0
- package/cpp/parser/TiptapASTNode.hpp +68 -0
- package/cpp/parser/TiptapParser.cpp +233 -0
- package/cpp/parser/TiptapParser.hpp +13 -0
- package/cpp/vendor/nlohmann/json.hpp +24765 -0
- package/ios/EnrichedTiptapText.h +27 -0
- package/ios/EnrichedTiptapText.mm +1222 -0
- package/ios/attachments/ENRMImageAttachment.h +51 -0
- package/ios/attachments/ENRMImageAttachment.m +398 -0
- package/ios/attachments/ENRMLinkPillAttachment.h +47 -0
- package/ios/attachments/ENRMLinkPillAttachment.m +324 -0
- package/ios/attachments/ENRMLinkPillIconCache.h +28 -0
- package/ios/attachments/ENRMLinkPillIconCache.m +181 -0
- package/ios/attachments/ENRMLinkPillText.h +27 -0
- package/ios/attachments/ENRMLinkPillText.m +118 -0
- package/ios/attachments/ENRMMathInlineAttachment.h +7 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +4 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/internals/ENRMViewFreeMeasurement.h +208 -0
- package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -0
- package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -0
- package/ios/internals/EnrichedTiptapTextState.h +24 -0
- package/ios/internals/MeasurementCache.h +308 -0
- package/ios/internals/ShadowMeasurementUtils.h +145 -0
- package/ios/parser/ENRMTextLinkAttributes.h +7 -0
- package/ios/parser/ENRMTiptapParser.h +33 -0
- package/ios/parser/ENRMTiptapParser.mm +73 -0
- package/ios/parser/TiptapASTNode.h +57 -0
- package/ios/parser/TiptapASTNode.m +52 -0
- package/ios/parser/TiptapParserBridge.mm +179 -0
- package/ios/renderer/AttributedRenderer.h +23 -0
- package/ios/renderer/AttributedRenderer.m +229 -0
- package/ios/renderer/BaseRenderer.h +33 -0
- package/ios/renderer/BaseRenderer.m +46 -0
- package/ios/renderer/BaselineShiftTextAttributes.h +10 -0
- package/ios/renderer/BaselineShiftTextAttributes.m +26 -0
- package/ios/renderer/BlankLineRenderer.h +4 -0
- package/ios/renderer/BlankLineRenderer.m +51 -0
- package/ios/renderer/BlockquoteRenderer.h +6 -0
- package/ios/renderer/BlockquoteRenderer.m +258 -0
- package/ios/renderer/CodeBlockRenderer.h +9 -0
- package/ios/renderer/CodeBlockRenderer.m +99 -0
- package/ios/renderer/CodeRenderer.h +9 -0
- package/ios/renderer/CodeRenderer.m +69 -0
- package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -0
- package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -0
- package/ios/renderer/EmphasisRenderer.h +5 -0
- package/ios/renderer/EmphasisRenderer.m +100 -0
- package/ios/renderer/FactoidRenderer.h +9 -0
- package/ios/renderer/FactoidRenderer.m +130 -0
- package/ios/renderer/HeadingRenderer.h +6 -0
- package/ios/renderer/HeadingRenderer.m +103 -0
- package/ios/renderer/HighlightRenderer.h +7 -0
- package/ios/renderer/HighlightRenderer.m +33 -0
- package/ios/renderer/LinkRenderer.h +6 -0
- package/ios/renderer/LinkRenderer.m +158 -0
- package/ios/renderer/ListItemRenderer.h +24 -0
- package/ios/renderer/ListItemRenderer.m +283 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +126 -0
- package/ios/renderer/MentionRenderer.h +9 -0
- package/ios/renderer/MentionRenderer.m +96 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +6 -0
- package/ios/renderer/ParagraphRenderer.m +85 -0
- package/ios/renderer/RenderContext.h +145 -0
- package/ios/renderer/RenderContext.m +403 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +158 -0
- package/ios/renderer/StrikethroughRenderer.h +5 -0
- package/ios/renderer/StrikethroughRenderer.m +30 -0
- package/ios/renderer/StrongRenderer.h +5 -0
- package/ios/renderer/StrongRenderer.m +88 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +18 -0
- package/ios/renderer/ThematicBreakRenderer.h +4 -0
- package/ios/renderer/ThematicBreakRenderer.m +42 -0
- package/ios/renderer/UnderlineRenderer.h +4 -0
- package/ios/renderer/UnderlineRenderer.m +41 -0
- package/ios/styles/ENRMFontSlot.h +12 -0
- package/ios/styles/ENRMFontSlot.m +24 -0
- package/ios/styles/StyleConfig.h +488 -0
- package/ios/styles/StyleConfig.mm +2820 -0
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/BlockquoteBorder.h +26 -0
- package/ios/utils/BlockquoteBorder.m +294 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +197 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +103 -0
- package/ios/utils/ContextMenuUtils.h +76 -0
- package/ios/utils/ContextMenuUtils.mm +75 -0
- package/ios/utils/ENRMAccessibilityLabels.h +40 -0
- package/ios/utils/ENRMAccessibilityLabels.m +5 -0
- package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -0
- package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -0
- package/ios/utils/ENRMAtomicSize.h +65 -0
- package/ios/utils/ENRMCodeBlockContent.h +37 -0
- package/ios/utils/ENRMCodeBlockContent.mm +89 -0
- package/ios/utils/ENRMFeatureFlags.h +25 -0
- package/ios/utils/ENRMImageDownloader.h +30 -0
- package/ios/utils/ENRMImageDownloader.m +168 -0
- package/ios/utils/ENRMLatexErrorCoordinator.h +27 -0
- package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -0
- package/ios/utils/ENRMLatexErrorReporting.h +13 -0
- package/ios/utils/ENRMLinkContextMenus.h +93 -0
- package/ios/utils/ENRMLinkContextMenus.m +178 -0
- package/ios/utils/ENRMLinkRegexConfig.h +30 -0
- package/ios/utils/ENRMLinkRegexConfig.mm +104 -0
- package/ios/utils/ENRMLinkRegexProps.h +23 -0
- package/ios/utils/ENRMLocalImageLoader.h +35 -0
- package/ios/utils/ENRMLocalImageLoader.m +111 -0
- package/ios/utils/ENRMMenuAction.h +27 -0
- package/ios/utils/ENRMMenuAction.m +33 -0
- package/ios/utils/ENRMSpoilerOverlayManager.h +15 -0
- package/ios/utils/ENRMSpoilerOverlayManager.m +19 -0
- package/ios/utils/ENRMSpoilerTapUtils.h +6 -0
- package/ios/utils/ENRMSpoilerTapUtils.m +11 -0
- package/ios/utils/ENRMTailFadeInAnimator.h +16 -0
- package/ios/utils/ENRMTailFadeInAnimator.m +161 -0
- package/ios/utils/ENRMTextHitTest.h +23 -0
- package/ios/utils/ENRMTextHitTest.m +20 -0
- package/ios/utils/ENRMTextInteractionUtils.h +20 -0
- package/ios/utils/ENRMTextInteractionUtils.m +15 -0
- package/ios/utils/ENRMTextRenderer.h +38 -0
- package/ios/utils/ENRMTextRenderer.m +54 -0
- package/ios/utils/ENRMTextViewSetup.h +132 -0
- package/ios/utils/ENRMUIKit.h +429 -0
- package/ios/utils/EditMenuUtils.h +105 -0
- package/ios/utils/EditMenuUtils.m +143 -0
- package/ios/utils/FactoidBackground.h +20 -0
- package/ios/utils/FactoidBackground.m +161 -0
- package/ios/utils/FontScaleObserver.h +10 -0
- package/ios/utils/FontScaleObserver.m +55 -0
- package/ios/utils/FontUtils.h +31 -0
- package/ios/utils/FontUtils.m +63 -0
- package/ios/utils/HTMLGenerator.h +25 -0
- package/ios/utils/HTMLGenerator.m +1041 -0
- package/ios/utils/HeightUpdateUtils.h +25 -0
- package/ios/utils/ImageRequestHeaderUtils.h +35 -0
- package/ios/utils/LastElementUtils.h +97 -0
- package/ios/utils/LinkContextMenuUtils.h +55 -0
- package/ios/utils/LinkPillContentUtils.h +46 -0
- package/ios/utils/LinkTapUtils.h +56 -0
- package/ios/utils/LinkTapUtils.m +139 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +239 -0
- package/ios/utils/MentionBackground.h +26 -0
- package/ios/utils/MentionBackground.m +86 -0
- package/ios/utils/ParagraphStyleUtils.h +87 -0
- package/ios/utils/ParagraphStyleUtils.m +484 -0
- package/ios/utils/PasteboardUtils.h +48 -0
- package/ios/utils/PasteboardUtils.m +166 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +299 -0
- package/ios/utils/RuntimeKeys.h +44 -0
- package/ios/utils/RuntimeKeys.m +13 -0
- package/ios/utils/SelectionColorUtils.h +14 -0
- package/ios/utils/StylePropsUtils.h +1340 -0
- package/ios/utils/TaskListTapUtils.h +6 -0
- package/ios/utils/TaskListTapUtils.m +14 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +198 -0
- package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -0
- package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -0
- package/ios/utils/TiptapExtractor.h +17 -0
- package/ios/utils/TiptapExtractor.m +367 -0
- package/ios/views/ENRMTiptapTextView.h +25 -0
- package/ios/views/ENRMTiptapTextView.m +247 -0
- package/package.json +70 -6
- package/react-native.config.js +10 -0
- package/src/EnrichedTiptapTextNativeComponent.ts +739 -0
- package/src/RichText.tsx +164 -0
- package/src/admonitionDefaults.ts +58 -0
- package/src/darkModeTextStyle.ts +99 -0
- package/src/headingDefaults.ts +21 -0
- package/src/index.tsx +4 -0
- package/src/linkVariantUtils.ts +120 -0
- package/src/normalizeTiptapStyle.ts +397 -0
- package/src/rn-globals.d.ts +1 -0
- package/src/styleUtils.ts +107 -0
- package/src/types/TiptapStyle.ts +532 -0
- package/src/types/TiptapStyleInternal.ts +299 -0
- package/src/types/linkPill.ts +9 -0
- package/src/utils/isDev.ts +4 -0
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import "ENRMUIKit.h"
|
|
3
|
+
|
|
4
|
+
#import "ENRMLinkContextMenus.h"
|
|
5
|
+
|
|
6
|
+
#if !TARGET_OS_OSX
|
|
7
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The read-only text view tiptap renders into. A link pill is one placeholder character
|
|
11
|
+
* in text storage; what the system reads for the selection (Look Up, Translate, Share,
|
|
12
|
+
* drag, the Copy key command) is answered here with the link text the pill stands for.
|
|
13
|
+
*
|
|
14
|
+
* It also presents the menu of a pill that has one, taken from a delegate that is an
|
|
15
|
+
* `ENRMLinkContextMenuSource`: UIKit lifts a pressed text item on a tinted box of its own,
|
|
16
|
+
* which hides the pill's look.
|
|
17
|
+
*/
|
|
18
|
+
@interface ENRMTiptapTextView : UITextView <ENRMLinkMenuPresenting>
|
|
19
|
+
/// Copies a selection the way the menu Copy does. Set by the hosting view, which knows the
|
|
20
|
+
/// source Tiptap and style; without it the Copy key command writes plain text and RTF only.
|
|
21
|
+
@property (nonatomic, copy, nullable) void (^copySelectionHandler)(NSRange selectedRange);
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
NS_ASSUME_NONNULL_END
|
|
25
|
+
#endif
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#import "ENRMTiptapTextView.h"
|
|
2
|
+
#import "ENRMLinkPillAttachment.h"
|
|
3
|
+
#import "ENRMLinkPillText.h"
|
|
4
|
+
#import "ENRMTextHitTest.h"
|
|
5
|
+
#import "LinkTapUtils.h"
|
|
6
|
+
#import "PasteboardUtils.h"
|
|
7
|
+
|
|
8
|
+
#if !TARGET_OS_OSX
|
|
9
|
+
@interface ENRMTiptapTextView () <UIContextMenuInteractionDelegate>
|
|
10
|
+
@end
|
|
11
|
+
|
|
12
|
+
@implementation ENRMTiptapTextView {
|
|
13
|
+
ENRMLinkMenuLift *_pillLift;
|
|
14
|
+
// The pill whose menu is being presented. It draws nothing for as long as its lifted image
|
|
15
|
+
// stands in for it: it would show through that image.
|
|
16
|
+
ENRMLinkPillAttachment *_liftedPill;
|
|
17
|
+
NSUInteger _pillMenuIndex;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
- (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
|
|
21
|
+
{
|
|
22
|
+
if (self = [super initWithFrame:frame textContainer:textContainer]) {
|
|
23
|
+
_pillLift = [[ENRMLinkMenuLift alloc] init];
|
|
24
|
+
__weak ENRMTiptapTextView *weakSelf = self;
|
|
25
|
+
_pillLift.onEnd = ^{ [weakSelf restoreLiftedPill]; };
|
|
26
|
+
[self addInteraction:[[UIContextMenuInteraction alloc] initWithDelegate:self]];
|
|
27
|
+
}
|
|
28
|
+
return self;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// A tap places the caret, and UITextView then scrolls that caret into view. This view is
|
|
32
|
+
/// already sized to its text and sits in the screen's scroll view, so that scroll shifts
|
|
33
|
+
/// the page and the tapped line jumps toward the top. Scrolling stays off, and these
|
|
34
|
+
/// requests are dropped. Copy and selection are unchanged.
|
|
35
|
+
- (void)setContentOffset:(CGPoint)contentOffset
|
|
36
|
+
{
|
|
37
|
+
if (!self.scrollEnabled && !CGPointEqualToPoint(contentOffset, CGPointZero)) {
|
|
38
|
+
if (!CGPointEqualToPoint(self.contentOffset, CGPointZero)) {
|
|
39
|
+
[super setContentOffset:CGPointZero];
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
[super setContentOffset:contentOffset];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
|
|
47
|
+
{
|
|
48
|
+
if (!self.scrollEnabled) {
|
|
49
|
+
[self setContentOffset:CGPointZero];
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
[super setContentOffset:contentOffset animated:animated];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
- (void)scrollRangeToVisible:(NSRange)range
|
|
56
|
+
{
|
|
57
|
+
if (self.scrollEnabled)
|
|
58
|
+
[super scrollRangeToVisible:range];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
|
|
62
|
+
{
|
|
63
|
+
if (self.scrollEnabled)
|
|
64
|
+
[super scrollRectToVisible:rect animated:animated];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// New text moves or replaces the pill under its lifted image.
|
|
68
|
+
- (void)setAttributedText:(NSAttributedString *)attributedText
|
|
69
|
+
{
|
|
70
|
+
[_pillLift end];
|
|
71
|
+
[super setAttributedText:attributedText];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// The selection with pills expanded, or nil when `range` is not the selection or holds no pill.
|
|
75
|
+
/// Only the selection is rewritten: UIKit also reads arbitrary ranges to find word boundaries,
|
|
76
|
+
/// and those must keep matching storage offsets.
|
|
77
|
+
- (nullable NSAttributedString *)expandedSelectionForRange:(UITextRange *)range
|
|
78
|
+
{
|
|
79
|
+
UITextRange *selection = self.selectedTextRange;
|
|
80
|
+
if (!range || !selection || selection.isEmpty || ![range isEqual:selection])
|
|
81
|
+
return nil;
|
|
82
|
+
NSRange selected = self.selectedRange;
|
|
83
|
+
NSTextStorage *storage = self.textStorage;
|
|
84
|
+
if (selected.location == NSNotFound || NSMaxRange(selected) > storage.length)
|
|
85
|
+
return nil;
|
|
86
|
+
NSAttributedString *text = [storage attributedSubstringFromRange:selected];
|
|
87
|
+
NSAttributedString *expanded = ENRMAttributedStringByExpandingLinkPills(text, NULL);
|
|
88
|
+
return expanded == text ? nil : expanded;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
- (NSString *)textInRange:(UITextRange *)range
|
|
92
|
+
{
|
|
93
|
+
return [self expandedSelectionForRange:range].string ?: [super textInRange:range];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
- (NSAttributedString *)attributedTextInRange:(UITextRange *)range
|
|
97
|
+
{
|
|
98
|
+
return [self expandedSelectionForRange:range] ?: [super attributedTextInRange:range];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// The menu's Copy is already replaced by the library's own action; this covers the key command.
|
|
102
|
+
- (void)copy:(id)sender
|
|
103
|
+
{
|
|
104
|
+
NSRange selected = self.selectedRange;
|
|
105
|
+
if (self.copySelectionHandler && selected.location != NSNotFound && selected.length > 0) {
|
|
106
|
+
self.copySelectionHandler(selected);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
NSAttributedString *expanded = [self expandedSelectionForRange:self.selectedTextRange];
|
|
110
|
+
if (!expanded) {
|
|
111
|
+
[super copy:sender];
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
copyAttributedStringToPasteboard(expanded, nil, nil);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#pragma mark - Pill menus
|
|
118
|
+
|
|
119
|
+
- (nullable ENRMLinkContextMenus *)linkContextMenus
|
|
120
|
+
{
|
|
121
|
+
id delegate = self.delegate;
|
|
122
|
+
return [delegate conformsToProtocol:@protocol(ENRMLinkContextMenuSource)]
|
|
123
|
+
? [(id<ENRMLinkContextMenuSource>)delegate linkContextMenusForTextView:self]
|
|
124
|
+
: nil;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// Where the pill at `index` is drawn, in the text view's coordinates.
|
|
128
|
+
- (CGRect)pillFrameAtIndex:(NSUInteger)index
|
|
129
|
+
{
|
|
130
|
+
NSLayoutManager *layoutManager = self.layoutManager;
|
|
131
|
+
NSUInteger glyph = [layoutManager glyphIndexForCharacterAtIndex:index];
|
|
132
|
+
CGSize size = [layoutManager attachmentSizeForGlyphAtIndex:glyph];
|
|
133
|
+
if (size.width <= 0 || size.height <= 0)
|
|
134
|
+
return CGRectNull;
|
|
135
|
+
CGRect line = [layoutManager lineFragmentRectForGlyphAtIndex:glyph effectiveRange:NULL];
|
|
136
|
+
// TextKit places an attachment glyph at the bottom edge of its bounds.
|
|
137
|
+
CGPoint location = [layoutManager locationForGlyphAtIndex:glyph];
|
|
138
|
+
UIEdgeInsets inset = self.textContainerInset;
|
|
139
|
+
return CGRectMake(inset.left + line.origin.x + location.x, inset.top + line.origin.y + location.y - size.height,
|
|
140
|
+
size.width, size.height);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Whether the character at `index` is a pill whose link has a menu.
|
|
144
|
+
- (BOOL)presentsLinkMenuAtIndex:(NSUInteger)index
|
|
145
|
+
{
|
|
146
|
+
ENRMLinkContextMenus *menus = [self linkContextMenus];
|
|
147
|
+
NSTextStorage *storage = self.textStorage;
|
|
148
|
+
if (menus.entries.count == 0 || index >= storage.length)
|
|
149
|
+
return NO;
|
|
150
|
+
id attachment = [storage attribute:NSAttachmentAttributeName atIndex:index effectiveRange:NULL];
|
|
151
|
+
return [attachment isKindOfClass:ENRMLinkPillAttachment.class] &&
|
|
152
|
+
[menus hasMenuForURL:linkURLAtRange(self, NSMakeRange(index, 1))];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// The character index of the pill under `point` when its link has a menu, else NSNotFound.
|
|
156
|
+
- (NSUInteger)menuPillIndexAtPoint:(CGPoint)point
|
|
157
|
+
{
|
|
158
|
+
if ([self linkContextMenus].entries.count == 0)
|
|
159
|
+
return NSNotFound;
|
|
160
|
+
NSUInteger index = ENRMCharacterIndexAtPoint(self, point);
|
|
161
|
+
if (index == NSNotFound || ![self presentsLinkMenuAtIndex:index] ||
|
|
162
|
+
!CGRectContainsPoint([self pillFrameAtIndex:index], point))
|
|
163
|
+
return NSNotFound;
|
|
164
|
+
return index;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
- (void)setPill:(ENRMLinkPillAttachment *)pill lifted:(BOOL)lifted
|
|
168
|
+
{
|
|
169
|
+
pill.lifted = lifted;
|
|
170
|
+
NSTextStorage *storage = self.textStorage;
|
|
171
|
+
[storage enumerateAttribute:NSAttachmentAttributeName
|
|
172
|
+
inRange:NSMakeRange(0, storage.length)
|
|
173
|
+
options:0
|
|
174
|
+
usingBlock:^(id value, NSRange range, BOOL *stop) {
|
|
175
|
+
if (value != pill)
|
|
176
|
+
return;
|
|
177
|
+
[self.layoutManager invalidateDisplayForCharacterRange:range];
|
|
178
|
+
*stop = YES;
|
|
179
|
+
}];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
- (void)restoreLiftedPill
|
|
183
|
+
{
|
|
184
|
+
if (_liftedPill)
|
|
185
|
+
[self setPill:_liftedPill lifted:NO];
|
|
186
|
+
_liftedPill = nil;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction
|
|
190
|
+
configurationForMenuAtLocation:(CGPoint)location
|
|
191
|
+
{
|
|
192
|
+
[_pillLift end];
|
|
193
|
+
_pillMenuIndex = [self menuPillIndexAtPoint:location];
|
|
194
|
+
if (_pillMenuIndex == NSNotFound)
|
|
195
|
+
return nil;
|
|
196
|
+
UIMenu *menu = [[self linkContextMenus] menuForURL:linkURLAtRange(self, NSMakeRange(_pillMenuIndex, 1))
|
|
197
|
+
title:linkTitleAtIndex(self.textStorage, _pillMenuIndex)];
|
|
198
|
+
if (!menu)
|
|
199
|
+
return nil;
|
|
200
|
+
return [UIContextMenuConfiguration
|
|
201
|
+
configurationWithIdentifier:nil
|
|
202
|
+
previewProvider:nil
|
|
203
|
+
actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) { return menu; }];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// The pill is lifted as an image of itself, see `ENRMLinkMenuLift`.
|
|
207
|
+
- (UITargetedPreview *)pillMenuPreview
|
|
208
|
+
{
|
|
209
|
+
if (![_pillLift isOverView:self]) {
|
|
210
|
+
NSTextStorage *storage = self.textStorage;
|
|
211
|
+
if (self.window == nil || _pillMenuIndex >= storage.length)
|
|
212
|
+
return nil;
|
|
213
|
+
id attachment = [storage attribute:NSAttachmentAttributeName atIndex:_pillMenuIndex effectiveRange:NULL];
|
|
214
|
+
CGRect frame = [self pillFrameAtIndex:_pillMenuIndex];
|
|
215
|
+
if (![attachment isKindOfClass:ENRMLinkPillAttachment.class] || CGRectIsNull(frame))
|
|
216
|
+
return nil;
|
|
217
|
+
UIImage *image = [(ENRMLinkPillAttachment *)attachment imageForBounds:(CGRect){CGPointZero, frame.size}
|
|
218
|
+
textContainer:self.textContainer
|
|
219
|
+
characterIndex:_pillMenuIndex];
|
|
220
|
+
[_pillLift layImage:image atFrame:frame overView:self];
|
|
221
|
+
_liftedPill = attachment;
|
|
222
|
+
[self setPill:_liftedPill lifted:YES];
|
|
223
|
+
}
|
|
224
|
+
return [_pillLift previewWithCornerRadius:_liftedPill.cornerRadius backgroundColor:UIColor.clearColor];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction
|
|
228
|
+
previewForHighlightingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
|
|
229
|
+
{
|
|
230
|
+
return [self pillMenuPreview];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction
|
|
234
|
+
previewForDismissingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration
|
|
235
|
+
{
|
|
236
|
+
return [self pillMenuPreview];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
- (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction
|
|
240
|
+
willEndForConfiguration:(UIContextMenuConfiguration *)configuration
|
|
241
|
+
animator:(id<UIContextMenuInteractionAnimating>)animator
|
|
242
|
+
{
|
|
243
|
+
[_pillLift endWithAnimator:animator];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@end
|
|
247
|
+
#endif
|