@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,168 @@
|
|
|
1
|
+
#import "ENRMImageDownloader.h"
|
|
2
|
+
#import "ENRMImageAttachment.h"
|
|
3
|
+
#import "ENRMLocalImageLoader.h"
|
|
4
|
+
#import <CommonCrypto/CommonDigest.h>
|
|
5
|
+
#include <TargetConditionals.h>
|
|
6
|
+
|
|
7
|
+
static const NSUInteger kDiskCacheMemoryCapacity = 10 * 1024 * 1024;
|
|
8
|
+
static const NSUInteger kDiskCacheDiskCapacity = 100 * 1024 * 1024;
|
|
9
|
+
|
|
10
|
+
NSUInteger ENRMImageByteCost(RCTUIImage *image)
|
|
11
|
+
{
|
|
12
|
+
CGImageRef cgImage = image.CGImage;
|
|
13
|
+
if (!cgImage)
|
|
14
|
+
return 0;
|
|
15
|
+
return CGImageGetBytesPerRow(cgImage) * CGImageGetHeight(cgImage);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static NSDictionary<NSString *, NSString *> *ENRMNormalizedImageHeaders(NSDictionary<NSString *, NSString *> *headers)
|
|
19
|
+
{
|
|
20
|
+
NSMutableDictionary<NSString *, NSString *> *normalized = [NSMutableDictionary dictionary];
|
|
21
|
+
for (NSString *name in [headers.allKeys sortedArrayUsingSelector:@selector(compare:)])
|
|
22
|
+
normalized[name.lowercaseString] = headers[name];
|
|
23
|
+
return normalized;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
NSString *ENRMImageCacheKey(NSString *url, NSDictionary<NSString *, NSString *> *headers)
|
|
27
|
+
{
|
|
28
|
+
if (headers.count == 0) {
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
NSDictionary<NSString *, NSString *> *normalized = ENRMNormalizedImageHeaders(headers);
|
|
32
|
+
NSArray<NSString *> *names = [normalized.allKeys sortedArrayUsingSelector:@selector(compare:)];
|
|
33
|
+
NSMutableArray<NSString *> *pairs = [NSMutableArray arrayWithCapacity:names.count];
|
|
34
|
+
for (NSString *name in names) {
|
|
35
|
+
[pairs addObject:[NSString stringWithFormat:@"%@:%@", name, normalized[name]]];
|
|
36
|
+
}
|
|
37
|
+
NSString *joined = [pairs componentsJoinedByString:@"\n"];
|
|
38
|
+
NSData *data = [joined dataUsingEncoding:NSUTF8StringEncoding];
|
|
39
|
+
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
|
|
40
|
+
CC_SHA256(data.bytes, (CC_LONG)data.length, digest);
|
|
41
|
+
NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
|
|
42
|
+
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
|
|
43
|
+
[hash appendFormat:@"%02x", digest[i]];
|
|
44
|
+
}
|
|
45
|
+
return [NSString stringWithFormat:@"%@|%@", url, hash];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@implementation ENRMImageDownloader {
|
|
49
|
+
NSURLSession *_session;
|
|
50
|
+
NSURLSession *_headerSession;
|
|
51
|
+
NSMutableDictionary<NSString *, NSMutableArray<ENRMImageDownloadCompletion> *> *_inFlightRequests;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
+ (instancetype)shared
|
|
55
|
+
{
|
|
56
|
+
static ENRMImageDownloader *instance;
|
|
57
|
+
static dispatch_once_t onceToken;
|
|
58
|
+
dispatch_once(&onceToken, ^{ instance = [[ENRMImageDownloader alloc] init]; });
|
|
59
|
+
return instance;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
- (instancetype)init
|
|
63
|
+
{
|
|
64
|
+
self = [super init];
|
|
65
|
+
if (self) {
|
|
66
|
+
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
67
|
+
config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:kDiskCacheMemoryCapacity
|
|
68
|
+
diskCapacity:kDiskCacheDiskCapacity
|
|
69
|
+
directoryURL:nil];
|
|
70
|
+
config.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
|
|
71
|
+
config.timeoutIntervalForRequest = 15;
|
|
72
|
+
config.timeoutIntervalForResource = 30;
|
|
73
|
+
_session = [NSURLSession sessionWithConfiguration:config];
|
|
74
|
+
// No URL cache: it keys by URL alone and would mix header sets.
|
|
75
|
+
NSURLSessionConfiguration *headerConfig = [config copy];
|
|
76
|
+
headerConfig.URLCache = nil;
|
|
77
|
+
_headerSession = [NSURLSession sessionWithConfiguration:headerConfig];
|
|
78
|
+
_inFlightRequests = [NSMutableDictionary dictionary];
|
|
79
|
+
}
|
|
80
|
+
return self;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
- (void)downloadURL:(NSString *)url
|
|
84
|
+
headers:(NSDictionary<NSString *, NSString *> *)headers
|
|
85
|
+
completion:(ENRMImageDownloadCompletion)completion
|
|
86
|
+
{
|
|
87
|
+
if (url.length == 0) {
|
|
88
|
+
completion(nil);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
headers = ENRMNormalizedImageHeaders(headers);
|
|
93
|
+
BOOL isLocal = ENRMIsLocalImageURL(url);
|
|
94
|
+
NSString *cacheKey = isLocal ? url : ENRMImageCacheKey(url, headers);
|
|
95
|
+
|
|
96
|
+
RCTUIImage *cached = [[ENRMImageAttachment originalImageCache] objectForKey:cacheKey];
|
|
97
|
+
if (cached) {
|
|
98
|
+
completion(cached);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (isLocal) {
|
|
103
|
+
RCTUIImage *local = ENRMLoadLocalImage(url);
|
|
104
|
+
if (local) {
|
|
105
|
+
[[ENRMImageAttachment originalImageCache] setObject:local forKey:cacheKey cost:ENRMImageByteCost(local)];
|
|
106
|
+
}
|
|
107
|
+
completion(local);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@synchronized(_inFlightRequests) {
|
|
112
|
+
NSMutableArray *existing = _inFlightRequests[cacheKey];
|
|
113
|
+
if (existing) {
|
|
114
|
+
[existing addObject:completion];
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
_inFlightRequests[cacheKey] = [NSMutableArray arrayWithObject:completion];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
NSURL *nsURL = [NSURL URLWithString:url];
|
|
121
|
+
if (!nsURL) {
|
|
122
|
+
[self dispatchCallbacksForKey:cacheKey image:nil];
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:nsURL];
|
|
127
|
+
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *name, NSString *value, BOOL *stop) {
|
|
128
|
+
[request setValue:value forHTTPHeaderField:name];
|
|
129
|
+
}];
|
|
130
|
+
|
|
131
|
+
NSURLSession *session = headers.count > 0 ? _headerSession : _session;
|
|
132
|
+
[[session dataTaskWithRequest:request
|
|
133
|
+
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
134
|
+
#if !TARGET_OS_OSX
|
|
135
|
+
RCTUIImage *image = (data && !error) ? [RCTUIImage imageWithData:data] : nil;
|
|
136
|
+
#else
|
|
137
|
+
RCTUIImage *image = (data && !error) ? [[RCTUIImage alloc] initWithData:data] : nil;
|
|
138
|
+
#endif
|
|
139
|
+
|
|
140
|
+
if (image) {
|
|
141
|
+
[[ENRMImageAttachment originalImageCache] setObject:image
|
|
142
|
+
forKey:cacheKey
|
|
143
|
+
cost:ENRMImageByteCost(image)];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
[self dispatchCallbacksForKey:cacheKey image:image];
|
|
147
|
+
}] resume];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
- (void)dispatchCallbacksForKey:(NSString *)cacheKey image:(RCTUIImage *_Nullable)image
|
|
151
|
+
{
|
|
152
|
+
NSArray<ENRMImageDownloadCompletion> *callbacks;
|
|
153
|
+
@synchronized(_inFlightRequests) {
|
|
154
|
+
callbacks = [_inFlightRequests[cacheKey] copy];
|
|
155
|
+
[_inFlightRequests removeObjectForKey:cacheKey];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!callbacks)
|
|
159
|
+
return;
|
|
160
|
+
|
|
161
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
162
|
+
for (ENRMImageDownloadCompletion cb in callbacks) {
|
|
163
|
+
cb(image);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import <Foundation/Foundation.h>
|
|
3
|
+
|
|
4
|
+
#import "ENRMLatexErrorReporting.h"
|
|
5
|
+
|
|
6
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
7
|
+
|
|
8
|
+
/// Returns YES when the error was delivered to a live event emitter, NO when it
|
|
9
|
+
/// must be queued and flushed once an emitter is attached.
|
|
10
|
+
typedef BOOL (^ENRMLatexErrorEmit)(NSString *source, NSString *message, BOOL displayMode);
|
|
11
|
+
|
|
12
|
+
/// Shared LaTeX-error plumbing for both host views. Owns the dedup set and the
|
|
13
|
+
/// pending queue and wires reporters; the only per-view difference - casting
|
|
14
|
+
/// `_eventEmitter` to the concrete type - stays in the `emit` block each view
|
|
15
|
+
/// supplies.
|
|
16
|
+
@interface ENRMLatexErrorCoordinator : NSObject
|
|
17
|
+
|
|
18
|
+
- (instancetype)initWithEmit:(ENRMLatexErrorEmit)emit NS_DESIGNATED_INITIALIZER;
|
|
19
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
20
|
+
|
|
21
|
+
- (void)wireReporters:(NSArray<id<ENRMLatexErrorReporting>> *)reporters;
|
|
22
|
+
- (void)reportSource:(NSString *)source message:(NSString *)message displayMode:(BOOL)displayMode;
|
|
23
|
+
- (void)flushPending;
|
|
24
|
+
|
|
25
|
+
@end
|
|
26
|
+
|
|
27
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#import "ENRMLatexErrorCoordinator.h"
|
|
2
|
+
|
|
3
|
+
@interface ENRMPendingLatexError : NSObject
|
|
4
|
+
@property (nonatomic, copy, readonly) NSString *source;
|
|
5
|
+
@property (nonatomic, copy, readonly) NSString *message;
|
|
6
|
+
@property (nonatomic, readonly) BOOL displayMode;
|
|
7
|
+
@end
|
|
8
|
+
|
|
9
|
+
@implementation ENRMPendingLatexError
|
|
10
|
+
- (instancetype)initWithSource:(NSString *)source message:(NSString *)message displayMode:(BOOL)displayMode
|
|
11
|
+
{
|
|
12
|
+
if (self = [super init]) {
|
|
13
|
+
_source = [source copy];
|
|
14
|
+
_message = [message copy];
|
|
15
|
+
_displayMode = displayMode;
|
|
16
|
+
}
|
|
17
|
+
return self;
|
|
18
|
+
}
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
@implementation ENRMLatexErrorCoordinator {
|
|
22
|
+
ENRMLatexErrorEmit _emit;
|
|
23
|
+
NSMutableSet<NSString *> *_reportedErrors;
|
|
24
|
+
NSMutableArray<ENRMPendingLatexError *> *_pendingErrors;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (instancetype)initWithEmit:(ENRMLatexErrorEmit)emit
|
|
28
|
+
{
|
|
29
|
+
if (self = [super init]) {
|
|
30
|
+
_emit = [emit copy];
|
|
31
|
+
_reportedErrors = [NSMutableSet set];
|
|
32
|
+
_pendingErrors = [NSMutableArray array];
|
|
33
|
+
}
|
|
34
|
+
return self;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
- (void)wireReporters:(NSArray<id<ENRMLatexErrorReporting>> *)reporters
|
|
38
|
+
{
|
|
39
|
+
if (reporters.count == 0)
|
|
40
|
+
return;
|
|
41
|
+
__weak __typeof(self) weakSelf = self;
|
|
42
|
+
ENRMLatexErrorHandler handler = ^(NSString *source, NSString *message, BOOL displayMode) {
|
|
43
|
+
[weakSelf reportSource:source message:message displayMode:displayMode];
|
|
44
|
+
};
|
|
45
|
+
for (id<ENRMLatexErrorReporting> reporter in reporters) {
|
|
46
|
+
reporter.onLatexError = handler;
|
|
47
|
+
[reporter reportLatexErrorIfNeeded];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)reportSource:(NSString *)source message:(NSString *)message displayMode:(BOOL)displayMode
|
|
52
|
+
{
|
|
53
|
+
NSString *key = [NSString stringWithFormat:@"%@ %@", displayMode ? @"B" : @"I", source];
|
|
54
|
+
if ([_reportedErrors containsObject:key])
|
|
55
|
+
return;
|
|
56
|
+
[_reportedErrors addObject:key];
|
|
57
|
+
if (!_emit(source, message, displayMode)) {
|
|
58
|
+
[_pendingErrors addObject:[[ENRMPendingLatexError alloc] initWithSource:source
|
|
59
|
+
message:message
|
|
60
|
+
displayMode:displayMode]];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
- (void)flushPending
|
|
65
|
+
{
|
|
66
|
+
if (_pendingErrors.count == 0)
|
|
67
|
+
return;
|
|
68
|
+
NSArray<ENRMPendingLatexError *> *pending = [_pendingErrors copy];
|
|
69
|
+
[_pendingErrors removeAllObjects];
|
|
70
|
+
for (ENRMPendingLatexError *e in pending) {
|
|
71
|
+
_emit(e.source, e.message, e.displayMode);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import <Foundation/Foundation.h>
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
typedef void (^ENRMLatexErrorHandler)(NSString *source, NSString *message, BOOL displayMode);
|
|
7
|
+
|
|
8
|
+
@protocol ENRMLatexErrorReporting <NSObject>
|
|
9
|
+
@property (nonatomic, copy, nullable) ENRMLatexErrorHandler onLatexError;
|
|
10
|
+
- (void)reportLatexErrorIfNeeded;
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import "ENRMUIKit.h"
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
/// One item of a link's long-press menu.
|
|
7
|
+
@interface ENRMLinkContextMenuItem : NSObject
|
|
8
|
+
@property (nonatomic, copy) NSString *text;
|
|
9
|
+
/// SF Symbol name; empty for no icon.
|
|
10
|
+
@property (nonatomic, copy) NSString *icon;
|
|
11
|
+
@property (nonatomic, assign) BOOL disabled;
|
|
12
|
+
@property (nonatomic, assign) BOOL destructive;
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
/// The menu items for links whose URL matches `pattern`.
|
|
16
|
+
@interface ENRMLinkContextMenuEntry : NSObject
|
|
17
|
+
@property (nonatomic, copy) NSString *pattern;
|
|
18
|
+
@property (nonatomic, copy) NSArray<ENRMLinkContextMenuItem *> *items;
|
|
19
|
+
@end
|
|
20
|
+
|
|
21
|
+
typedef void (^ENRMLinkContextMenuPressHandler)(NSString *url, NSString *pattern, NSString *itemText);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The `linkContextMenuItems` prop: long-press menus for links, by URL pattern.
|
|
25
|
+
* Entries are tried in order, like link variants, and the first whose pattern
|
|
26
|
+
* matches a URL supplies its menu. Menus need iOS 17; below that no URL has one.
|
|
27
|
+
*/
|
|
28
|
+
@interface ENRMLinkContextMenus : NSObject
|
|
29
|
+
@property (nonatomic, copy) NSArray<ENRMLinkContextMenuEntry *> *entries;
|
|
30
|
+
@property (nonatomic, copy, nullable) ENRMLinkContextMenuPressHandler onPress;
|
|
31
|
+
- (BOOL)hasMenuForURL:(nullable NSString *)url;
|
|
32
|
+
#if !TARGET_OS_OSX
|
|
33
|
+
/// `title` is what the user sees the link as: its text, or its pill label.
|
|
34
|
+
- (nullable UIMenu *)menuForURL:(nullable NSString *)url title:(nullable NSString *)title;
|
|
35
|
+
#endif
|
|
36
|
+
@end
|
|
37
|
+
|
|
38
|
+
#if !TARGET_OS_OSX
|
|
39
|
+
/// Adopted by a text view's delegate to supply the long-press menus of its links.
|
|
40
|
+
@protocol ENRMLinkContextMenuSource <NSObject>
|
|
41
|
+
- (nullable ENRMLinkContextMenus *)linkContextMenusForTextView:(UITextView *)textView;
|
|
42
|
+
@end
|
|
43
|
+
|
|
44
|
+
/// Adopted by a text view that presents the menus of some of its links itself.
|
|
45
|
+
@protocol ENRMLinkMenuPresenting <NSObject>
|
|
46
|
+
/// UIKit must be given no menu for such a link, or it lifts the link as a text item as well.
|
|
47
|
+
- (BOOL)presentsLinkMenuAtIndex:(NSUInteger)index;
|
|
48
|
+
@end
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The copy of a link that its long-press menu lifts.
|
|
52
|
+
*
|
|
53
|
+
* UIKit draws a menu's preview only from a view that is on screen, and hides that view for as
|
|
54
|
+
* long as the menu is open. Lifting part of a larger view through a visible path would hide
|
|
55
|
+
* all of it, and a detached view stays blank until the menu appears. So the link is lifted as
|
|
56
|
+
* an image of itself, laid over the view it belongs to and taken down when the menu ends.
|
|
57
|
+
*/
|
|
58
|
+
@interface ENRMLinkMenuLift : NSObject
|
|
59
|
+
/// Called when the image is taken down, for the owner to restore what the image stood in for.
|
|
60
|
+
@property (nonatomic, copy, nullable) void (^onEnd)(void);
|
|
61
|
+
/// Whether an image is laid over `view`.
|
|
62
|
+
- (BOOL)isOverView:(UIView *)view;
|
|
63
|
+
/// Takes down the image laid before, if any.
|
|
64
|
+
- (void)layImage:(UIImage *)image atFrame:(CGRect)frame overView:(UIView *)view;
|
|
65
|
+
/// The preview that lifts the laid image, nil when there is none. A nil `backgroundColor`
|
|
66
|
+
/// leaves UIKit's own behind the image.
|
|
67
|
+
- (nullable UITargetedPreview *)previewWithCornerRadius:(CGFloat)cornerRadius
|
|
68
|
+
backgroundColor:(nullable UIColor *)backgroundColor;
|
|
69
|
+
- (void)end;
|
|
70
|
+
/// Ends once the menu has animated away, unless another image was laid in the meantime.
|
|
71
|
+
- (void)endWithAnimator:(nullable id<UIContextMenuInteractionAnimating>)animator;
|
|
72
|
+
@end
|
|
73
|
+
|
|
74
|
+
#ifdef __cplusplus
|
|
75
|
+
extern "C" {
|
|
76
|
+
#endif
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* What a long press on a text item shows: the link's configured menu, or what links did before
|
|
80
|
+
* menus existed (the system menu with a preview, or `onLinkLongPress` when previews are off).
|
|
81
|
+
* Every text view delegate answers `textView:menuConfigurationForTextItem:defaultMenu:` with this.
|
|
82
|
+
* A link whose menu the text view presents itself (`ENRMLinkMenuPresenting`) gets none here.
|
|
83
|
+
*/
|
|
84
|
+
UITextItemMenuConfiguration *_Nullable ENRMLinkMenuConfigurationForTextItem(
|
|
85
|
+
UITextView *textView, UITextItem *textItem, UIMenu *defaultMenu, ENRMLinkContextMenus *_Nullable menus,
|
|
86
|
+
BOOL linkPreviewEnabled, void (^_Nullable onLinkLongPress)(NSString *url)) API_AVAILABLE(ios(17.0));
|
|
87
|
+
|
|
88
|
+
#ifdef __cplusplus
|
|
89
|
+
}
|
|
90
|
+
#endif
|
|
91
|
+
#endif
|
|
92
|
+
|
|
93
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#import "ENRMLinkContextMenus.h"
|
|
2
|
+
#import "LinkTapUtils.h"
|
|
3
|
+
|
|
4
|
+
@implementation ENRMLinkContextMenuItem
|
|
5
|
+
@end
|
|
6
|
+
|
|
7
|
+
@implementation ENRMLinkContextMenuEntry
|
|
8
|
+
@end
|
|
9
|
+
|
|
10
|
+
@implementation ENRMLinkContextMenus {
|
|
11
|
+
NSArray<NSRegularExpression *> *_regexes;
|
|
12
|
+
// A long press asks about the same URL several times (gesture gating, then the menu).
|
|
13
|
+
NSMutableDictionary<NSString *, id> *_entriesByURL;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
- (instancetype)init
|
|
17
|
+
{
|
|
18
|
+
if (self = [super init]) {
|
|
19
|
+
_entries = @[];
|
|
20
|
+
_regexes = @[];
|
|
21
|
+
_entriesByURL = [NSMutableDictionary new];
|
|
22
|
+
}
|
|
23
|
+
return self;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (void)setEntries:(NSArray<ENRMLinkContextMenuEntry *> *)entries
|
|
27
|
+
{
|
|
28
|
+
NSMutableArray<ENRMLinkContextMenuEntry *> *valid = [NSMutableArray arrayWithCapacity:entries.count];
|
|
29
|
+
NSMutableArray<NSRegularExpression *> *regexes = [NSMutableArray arrayWithCapacity:entries.count];
|
|
30
|
+
for (ENRMLinkContextMenuEntry *entry in entries) {
|
|
31
|
+
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:entry.pattern options:0 error:nil];
|
|
32
|
+
if (!regex || entry.items.count == 0)
|
|
33
|
+
continue;
|
|
34
|
+
[valid addObject:entry];
|
|
35
|
+
[regexes addObject:regex];
|
|
36
|
+
}
|
|
37
|
+
_entries = [valid copy];
|
|
38
|
+
_regexes = [regexes copy];
|
|
39
|
+
[_entriesByURL removeAllObjects];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
- (nullable ENRMLinkContextMenuEntry *)entryForURL:(NSString *)url
|
|
43
|
+
{
|
|
44
|
+
if (url.length == 0 || _entries.count == 0)
|
|
45
|
+
return nil;
|
|
46
|
+
id cached = _entriesByURL[url];
|
|
47
|
+
if (cached)
|
|
48
|
+
return cached == NSNull.null ? nil : cached;
|
|
49
|
+
|
|
50
|
+
ENRMLinkContextMenuEntry *match = nil;
|
|
51
|
+
NSRange whole = NSMakeRange(0, url.length);
|
|
52
|
+
for (NSUInteger index = 0; index < _regexes.count; index++) {
|
|
53
|
+
if ([_regexes[index] rangeOfFirstMatchInString:url options:0 range:whole].location != NSNotFound) {
|
|
54
|
+
match = _entries[index];
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
_entriesByURL[url] = match ?: NSNull.null;
|
|
59
|
+
return match;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
- (BOOL)hasMenuForURL:(NSString *)url
|
|
63
|
+
{
|
|
64
|
+
#if !TARGET_OS_OSX
|
|
65
|
+
if (@available(iOS 17.0, *))
|
|
66
|
+
return [self entryForURL:url] != nil;
|
|
67
|
+
#endif
|
|
68
|
+
return NO;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#if !TARGET_OS_OSX
|
|
72
|
+
- (UIMenu *)menuForURL:(NSString *)url title:(NSString *)title
|
|
73
|
+
{
|
|
74
|
+
if (![self hasMenuForURL:url])
|
|
75
|
+
return nil;
|
|
76
|
+
ENRMLinkContextMenuEntry *entry = [self entryForURL:url];
|
|
77
|
+
NSString *pattern = entry.pattern;
|
|
78
|
+
NSMutableArray<UIAction *> *actions = [NSMutableArray arrayWithCapacity:entry.items.count];
|
|
79
|
+
__weak ENRMLinkContextMenus *weakSelf = self;
|
|
80
|
+
for (ENRMLinkContextMenuItem *item in entry.items) {
|
|
81
|
+
NSString *text = item.text;
|
|
82
|
+
UIAction *action = [UIAction actionWithTitle:text
|
|
83
|
+
image:item.icon.length > 0 ? [UIImage systemImageNamed:item.icon] : nil
|
|
84
|
+
identifier:nil
|
|
85
|
+
handler:^(__kindof UIAction *pressed) {
|
|
86
|
+
ENRMLinkContextMenuPressHandler onPress = weakSelf.onPress;
|
|
87
|
+
if (onPress)
|
|
88
|
+
onPress(url, pattern, text);
|
|
89
|
+
}];
|
|
90
|
+
if (item.disabled)
|
|
91
|
+
action.attributes |= UIMenuElementAttributesDisabled;
|
|
92
|
+
if (item.destructive)
|
|
93
|
+
action.attributes |= UIMenuElementAttributesDestructive;
|
|
94
|
+
[actions addObject:action];
|
|
95
|
+
}
|
|
96
|
+
return [UIMenu menuWithTitle:title ?: @"" children:actions];
|
|
97
|
+
}
|
|
98
|
+
#endif
|
|
99
|
+
@end
|
|
100
|
+
|
|
101
|
+
#if !TARGET_OS_OSX
|
|
102
|
+
@implementation ENRMLinkMenuLift {
|
|
103
|
+
UIImageView *_imageView;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
- (BOOL)isOverView:(UIView *)view
|
|
107
|
+
{
|
|
108
|
+
return _imageView != nil && _imageView.superview == view;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
- (void)layImage:(UIImage *)image atFrame:(CGRect)frame overView:(UIView *)view
|
|
112
|
+
{
|
|
113
|
+
[self end];
|
|
114
|
+
_imageView = [[UIImageView alloc] initWithImage:image];
|
|
115
|
+
_imageView.frame = frame;
|
|
116
|
+
[view addSubview:_imageView];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
- (UITargetedPreview *)previewWithCornerRadius:(CGFloat)cornerRadius backgroundColor:(UIColor *)backgroundColor
|
|
120
|
+
{
|
|
121
|
+
if (_imageView.window == nil)
|
|
122
|
+
return nil;
|
|
123
|
+
UIPreviewParameters *parameters = [[UIPreviewParameters alloc] init];
|
|
124
|
+
if (backgroundColor)
|
|
125
|
+
parameters.backgroundColor = backgroundColor;
|
|
126
|
+
parameters.visiblePath = [UIBezierPath bezierPathWithRoundedRect:_imageView.bounds cornerRadius:cornerRadius];
|
|
127
|
+
return [[UITargetedPreview alloc] initWithView:_imageView parameters:parameters];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
- (void)end
|
|
131
|
+
{
|
|
132
|
+
if (!_imageView)
|
|
133
|
+
return;
|
|
134
|
+
[_imageView removeFromSuperview];
|
|
135
|
+
_imageView = nil;
|
|
136
|
+
if (self.onEnd)
|
|
137
|
+
self.onEnd();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (void)endWithAnimator:(id<UIContextMenuInteractionAnimating>)animator
|
|
141
|
+
{
|
|
142
|
+
UIImageView *laid = _imageView;
|
|
143
|
+
if (!laid)
|
|
144
|
+
return;
|
|
145
|
+
__weak ENRMLinkMenuLift *weakSelf = self;
|
|
146
|
+
void (^end)(void) = ^{
|
|
147
|
+
ENRMLinkMenuLift *strongSelf = weakSelf;
|
|
148
|
+
if (strongSelf && strongSelf->_imageView == laid)
|
|
149
|
+
[strongSelf end];
|
|
150
|
+
};
|
|
151
|
+
if (animator)
|
|
152
|
+
[animator addCompletion:end];
|
|
153
|
+
else
|
|
154
|
+
end();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@end
|
|
158
|
+
|
|
159
|
+
UITextItemMenuConfiguration *ENRMLinkMenuConfigurationForTextItem(UITextView *textView, UITextItem *textItem,
|
|
160
|
+
UIMenu *defaultMenu, ENRMLinkContextMenus *menus,
|
|
161
|
+
BOOL linkPreviewEnabled,
|
|
162
|
+
void (^onLinkLongPress)(NSString *url))
|
|
163
|
+
{
|
|
164
|
+
if ([textView conformsToProtocol:@protocol(ENRMLinkMenuPresenting)] &&
|
|
165
|
+
[(id<ENRMLinkMenuPresenting>)textView presentsLinkMenuAtIndex:textItem.range.location])
|
|
166
|
+
return nil;
|
|
167
|
+
NSString *url = linkURLAtRange(textView, textItem.range);
|
|
168
|
+
UIMenu *menu = [menus menuForURL:url title:linkTitleAtIndex(textView.textStorage, textItem.range.location)];
|
|
169
|
+
if (menu)
|
|
170
|
+
return [UITextItemMenuConfiguration configurationWithMenu:menu];
|
|
171
|
+
if (url && !linkPreviewEnabled) {
|
|
172
|
+
if (onLinkLongPress)
|
|
173
|
+
onLinkLongPress(url);
|
|
174
|
+
return nil;
|
|
175
|
+
}
|
|
176
|
+
return [UITextItemMenuConfiguration configurationWithMenu:defaultMenu];
|
|
177
|
+
}
|
|
178
|
+
#endif
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
@interface ENRMLinkRegexConfig : NSObject
|
|
8
|
+
|
|
9
|
+
@property (nonatomic, copy, readonly) NSString *pattern;
|
|
10
|
+
@property (nonatomic, assign, readonly) BOOL caseInsensitive;
|
|
11
|
+
@property (nonatomic, assign, readonly) BOOL dotAll;
|
|
12
|
+
@property (nonatomic, assign, readonly) BOOL isDisabled;
|
|
13
|
+
@property (nonatomic, assign, readonly) BOOL isDefault;
|
|
14
|
+
@property (nonatomic, strong, readonly, nullable) NSRegularExpression *parsedRegex;
|
|
15
|
+
@property (nonatomic, strong, readonly, nullable) NSRegularExpression *parsedWholeSpanRegex;
|
|
16
|
+
|
|
17
|
+
- (instancetype)initWithPattern:(NSString *)pattern
|
|
18
|
+
caseInsensitive:(BOOL)caseInsensitive
|
|
19
|
+
dotAll:(BOOL)dotAll
|
|
20
|
+
isDisabled:(BOOL)isDisabled
|
|
21
|
+
isDefault:(BOOL)isDefault;
|
|
22
|
+
|
|
23
|
+
/// Shared instance per pattern and flags; the renderer's props go through here. Thread-safe.
|
|
24
|
+
+ (instancetype)cachedConfigWithPattern:(NSString *)pattern caseInsensitive:(BOOL)caseInsensitive dotAll:(BOOL)dotAll;
|
|
25
|
+
|
|
26
|
+
- (BOOL)isEqualToConfig:(nullable ENRMLinkRegexConfig *)other;
|
|
27
|
+
|
|
28
|
+
@end
|
|
29
|
+
|
|
30
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#import "ENRMLinkRegexConfig.h"
|
|
2
|
+
|
|
3
|
+
#import <React/RCTLog.h>
|
|
4
|
+
|
|
5
|
+
@implementation ENRMLinkRegexConfig {
|
|
6
|
+
NSRegularExpression *_parsedWholeSpanRegex;
|
|
7
|
+
BOOL _wholeSpanResolved;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
- (instancetype)initWithPattern:(NSString *)pattern
|
|
11
|
+
caseInsensitive:(BOOL)caseInsensitive
|
|
12
|
+
dotAll:(BOOL)dotAll
|
|
13
|
+
isDisabled:(BOOL)isDisabled
|
|
14
|
+
isDefault:(BOOL)isDefault
|
|
15
|
+
{
|
|
16
|
+
self = [super init];
|
|
17
|
+
if (!self) {
|
|
18
|
+
return nil;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_pattern = [pattern copy];
|
|
22
|
+
_caseInsensitive = caseInsensitive;
|
|
23
|
+
_dotAll = dotAll;
|
|
24
|
+
_isDisabled = isDisabled;
|
|
25
|
+
_isDefault = isDefault;
|
|
26
|
+
_parsedRegex = nil;
|
|
27
|
+
|
|
28
|
+
if (!_isDefault && !_isDisabled && _pattern.length > 0) {
|
|
29
|
+
NSError *error = nil;
|
|
30
|
+
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:_pattern
|
|
31
|
+
options:[self options]
|
|
32
|
+
error:&error];
|
|
33
|
+
if (error) {
|
|
34
|
+
RCTLogWarn(@"[EnrichedTiptap]: The link regex '%@' is not a valid NSRegularExpression and is ignored.",
|
|
35
|
+
_pattern);
|
|
36
|
+
} else {
|
|
37
|
+
_parsedRegex = regex;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return self;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
+ (instancetype)cachedConfigWithPattern:(NSString *)pattern caseInsensitive:(BOOL)caseInsensitive dotAll:(BOOL)dotAll
|
|
45
|
+
{
|
|
46
|
+
static NSCache<NSString *, ENRMLinkRegexConfig *> *cache;
|
|
47
|
+
static dispatch_once_t onceToken;
|
|
48
|
+
dispatch_once(&onceToken, ^{
|
|
49
|
+
cache = [[NSCache alloc] init];
|
|
50
|
+
cache.countLimit = 64;
|
|
51
|
+
});
|
|
52
|
+
NSString *key = [NSString stringWithFormat:@"%d%d%@", caseInsensitive, dotAll, pattern];
|
|
53
|
+
ENRMLinkRegexConfig *config = [cache objectForKey:key];
|
|
54
|
+
if (!config) {
|
|
55
|
+
config = [[self alloc] initWithPattern:pattern
|
|
56
|
+
caseInsensitive:caseInsensitive
|
|
57
|
+
dotAll:dotAll
|
|
58
|
+
isDisabled:NO
|
|
59
|
+
isDefault:NO];
|
|
60
|
+
[cache setObject:config forKey:key];
|
|
61
|
+
}
|
|
62
|
+
return config;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
- (NSRegularExpressionOptions)options
|
|
66
|
+
{
|
|
67
|
+
NSRegularExpressionOptions options = 0;
|
|
68
|
+
if (_caseInsensitive) {
|
|
69
|
+
options |= NSRegularExpressionCaseInsensitive;
|
|
70
|
+
}
|
|
71
|
+
if (_dotAll) {
|
|
72
|
+
options |= NSRegularExpressionDotMatchesLineSeparators;
|
|
73
|
+
}
|
|
74
|
+
return options;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
- (NSRegularExpression *)parsedWholeSpanRegex
|
|
78
|
+
{
|
|
79
|
+
if (_parsedRegex == nil) {
|
|
80
|
+
return nil;
|
|
81
|
+
}
|
|
82
|
+
@synchronized(self) {
|
|
83
|
+
if (!_wholeSpanResolved) {
|
|
84
|
+
// Anchor the whole alternation so `a|ab` matches "ab" in full.
|
|
85
|
+
_parsedWholeSpanRegex =
|
|
86
|
+
[NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"\\A(?:%@)\\z", _pattern]
|
|
87
|
+
options:[self options]
|
|
88
|
+
error:nil];
|
|
89
|
+
_wholeSpanResolved = YES;
|
|
90
|
+
}
|
|
91
|
+
return _parsedWholeSpanRegex;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (BOOL)isEqualToConfig:(ENRMLinkRegexConfig *)other
|
|
96
|
+
{
|
|
97
|
+
if (other == nil) {
|
|
98
|
+
return NO;
|
|
99
|
+
}
|
|
100
|
+
return [_pattern isEqualToString:other.pattern] && _caseInsensitive == other.caseInsensitive &&
|
|
101
|
+
_dotAll == other.dotAll && _isDefault == other.isDefault && _isDisabled == other.isDisabled;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@end
|