@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,133 @@
|
|
|
1
|
+
package com.renderrichtext.renderer
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableStringBuilder
|
|
4
|
+
import com.renderrichtext.parser.TiptapASTNode
|
|
5
|
+
import com.renderrichtext.spans.ListItemSpacingSpan
|
|
6
|
+
import com.renderrichtext.spans.MarginBottomSpan
|
|
7
|
+
import com.renderrichtext.spans.OrderedListSpan
|
|
8
|
+
import com.renderrichtext.styles.ListStyle
|
|
9
|
+
import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
10
|
+
import com.renderrichtext.utils.text.span.applyLineHeightSkippingImages
|
|
11
|
+
import com.renderrichtext.utils.text.span.applyMarginTop
|
|
12
|
+
|
|
13
|
+
class ListRenderer(
|
|
14
|
+
private val config: RendererConfig,
|
|
15
|
+
private val isOrdered: Boolean,
|
|
16
|
+
) : NodeRenderer {
|
|
17
|
+
override fun render(
|
|
18
|
+
node: TiptapASTNode,
|
|
19
|
+
builder: SpannableStringBuilder,
|
|
20
|
+
onLinkPress: ((String) -> Unit)?,
|
|
21
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
22
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
23
|
+
factory: RendererFactory,
|
|
24
|
+
) {
|
|
25
|
+
val start = builder.length
|
|
26
|
+
val listStyle = config.style.listStyle
|
|
27
|
+
val listType = if (isOrdered) BlockStyleContext.ListType.ORDERED else BlockStyleContext.ListType.UNORDERED
|
|
28
|
+
|
|
29
|
+
val startNumber =
|
|
30
|
+
if (isOrdered) {
|
|
31
|
+
node.getAttribute("start")?.toIntOrNull()?.coerceAtLeast(0) ?: 1
|
|
32
|
+
} else {
|
|
33
|
+
1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
val markerExtraWidth = if (isOrdered) markerExtraWidth(node, listStyle, startNumber, factory) else 0f
|
|
37
|
+
val contextManager = ListContextManager(factory.blockStyleContext)
|
|
38
|
+
val entryState = contextManager.enterList(listType, listStyle, startNumber, markerExtraWidth)
|
|
39
|
+
|
|
40
|
+
// For top-level lists, insert marginTop spacer before rendering content
|
|
41
|
+
if (entryState.previousDepth == 0) {
|
|
42
|
+
applyMarginTop(builder, start, listStyle.marginTop)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Track start index after the potential 1-character marginTop spacer
|
|
46
|
+
val contentStart = if (entryState.previousDepth == 0 && listStyle.marginTop > 0) start + 1 else start
|
|
47
|
+
|
|
48
|
+
// Ensure nested lists start on a new line if the parent hasn't provided one
|
|
49
|
+
if (entryState.previousDepth > 0 && builder.isNotEmpty() && builder.last() != '\n') {
|
|
50
|
+
builder.append("\n")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
|
|
55
|
+
} finally {
|
|
56
|
+
contextManager.exitList(entryState)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (builder.length > contentStart) {
|
|
60
|
+
applyListSpacing(builder, contentStart, entryState.previousDepth, listStyle, factory.blockStyleContext)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Lists under 100 keep the `99.` column, so only numbers from 100 on widen it. Task items draw a checkbox. */
|
|
65
|
+
private fun markerExtraWidth(
|
|
66
|
+
node: TiptapASTNode,
|
|
67
|
+
listStyle: ListStyle,
|
|
68
|
+
startNumber: Int,
|
|
69
|
+
factory: RendererFactory,
|
|
70
|
+
): Float {
|
|
71
|
+
val wideNumbers = mutableListOf<Int>()
|
|
72
|
+
var number = startNumber
|
|
73
|
+
for (item in node.children) {
|
|
74
|
+
if (item.type != TiptapASTNode.NodeType.ListItem) continue
|
|
75
|
+
if (number >= 100 && item.attributes["isTask"] != "true") wideNumbers += number
|
|
76
|
+
number++
|
|
77
|
+
}
|
|
78
|
+
return OrderedListSpan.measureExtraWidth(listStyle, factory.context, wideNumbers)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private fun applyListSpacing(
|
|
82
|
+
builder: SpannableStringBuilder,
|
|
83
|
+
start: Int,
|
|
84
|
+
depth: Int,
|
|
85
|
+
style: ListStyle,
|
|
86
|
+
styleContext: BlockStyleContext,
|
|
87
|
+
) {
|
|
88
|
+
// TODO: LineHeightSpan may also clip superscript/subscript glyphs that extend
|
|
89
|
+
// outside the normal line bounds. A similar "skip" strategy as for images may
|
|
90
|
+
// be needed here once super/subscript usage in practice is better understood.
|
|
91
|
+
applyLineHeightSkippingImages(builder, start, builder.length, style.lineHeight)
|
|
92
|
+
|
|
93
|
+
// Item spacing and external bottom margin are only handled by the root-level list
|
|
94
|
+
if (depth == 0) {
|
|
95
|
+
applyItemSpacing(builder, start, style.itemSpacing, styleContext)
|
|
96
|
+
|
|
97
|
+
builder.append("\n")
|
|
98
|
+
builder.setSpan(
|
|
99
|
+
MarginBottomSpan(style.marginBottom),
|
|
100
|
+
builder.length - 1,
|
|
101
|
+
builder.length,
|
|
102
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Inserts the vertical gap between consecutive items (including nested ones)
|
|
108
|
+
// by spanning the newline that precedes each item start. The first item of
|
|
109
|
+
// the root list gets no spacing above it.
|
|
110
|
+
private fun applyItemSpacing(
|
|
111
|
+
builder: SpannableStringBuilder,
|
|
112
|
+
start: Int,
|
|
113
|
+
itemSpacing: Float,
|
|
114
|
+
styleContext: BlockStyleContext,
|
|
115
|
+
) {
|
|
116
|
+
if (itemSpacing <= 0f) return
|
|
117
|
+
|
|
118
|
+
val itemStarts =
|
|
119
|
+
styleContext.listItemStarts
|
|
120
|
+
.filter { it in start until builder.length }
|
|
121
|
+
.distinct()
|
|
122
|
+
.sorted()
|
|
123
|
+
|
|
124
|
+
for (itemStart in itemStarts.drop(1)) {
|
|
125
|
+
builder.setSpan(
|
|
126
|
+
ListItemSpacingSpan(itemSpacing),
|
|
127
|
+
itemStart - 1,
|
|
128
|
+
itemStart,
|
|
129
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
package com.renderrichtext.renderer
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableStringBuilder
|
|
4
|
+
import com.facebook.react.uimanager.PixelUtil
|
|
5
|
+
import com.renderrichtext.parser.TiptapASTNode
|
|
6
|
+
import com.renderrichtext.spans.MentionChipSpan
|
|
7
|
+
import com.renderrichtext.spans.MentionSpan
|
|
8
|
+
import com.renderrichtext.spans.TextSpan
|
|
9
|
+
import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tiptap mention atom. The node has no children; the visible text is `@` plus
|
|
13
|
+
* `label`, or `id` when `label` is blank. That is the suggestion character the
|
|
14
|
+
* Mention extension inserts.
|
|
15
|
+
*
|
|
16
|
+
* Weight is medium (`500`), the Android face used for a name inside body text.
|
|
17
|
+
* iOS uses semibold for the same role.
|
|
18
|
+
*/
|
|
19
|
+
class MentionRenderer(
|
|
20
|
+
private val config: RendererConfig,
|
|
21
|
+
) : NodeRenderer {
|
|
22
|
+
override fun render(
|
|
23
|
+
node: TiptapASTNode,
|
|
24
|
+
builder: SpannableStringBuilder,
|
|
25
|
+
onLinkPress: ((String) -> Unit)?,
|
|
26
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
27
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
28
|
+
factory: RendererFactory,
|
|
29
|
+
) {
|
|
30
|
+
val mentionId = node.getAttribute("id").orEmpty()
|
|
31
|
+
val label = node.getAttribute("label").orEmpty()
|
|
32
|
+
val name = label.ifBlank { mentionId }
|
|
33
|
+
val text = "@$name"
|
|
34
|
+
val mentionStyle = config.style.mentionStyle
|
|
35
|
+
val block = factory.blockStyleContext.currentBlockStyleOrNull()
|
|
36
|
+
val fontSize = block?.fontSize ?: config.style.paragraphStyle.fontSize
|
|
37
|
+
val fontFamily = block?.fontFamily ?: config.style.paragraphStyle.fontFamily
|
|
38
|
+
|
|
39
|
+
val start = builder.length
|
|
40
|
+
builder.append(text)
|
|
41
|
+
val end = builder.length
|
|
42
|
+
builder.setSpan(
|
|
43
|
+
TextSpan(
|
|
44
|
+
BlockStyle(
|
|
45
|
+
fontSize = fontSize,
|
|
46
|
+
fontFamily = fontFamily,
|
|
47
|
+
fontWeight = "500",
|
|
48
|
+
color = mentionStyle.color,
|
|
49
|
+
),
|
|
50
|
+
factory.context,
|
|
51
|
+
),
|
|
52
|
+
start,
|
|
53
|
+
end,
|
|
54
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
55
|
+
)
|
|
56
|
+
builder.setSpan(
|
|
57
|
+
MentionSpan(
|
|
58
|
+
mentionId,
|
|
59
|
+
label,
|
|
60
|
+
mentionStyle.color,
|
|
61
|
+
factory.onMentionPress,
|
|
62
|
+
factory.onMentionLongPress,
|
|
63
|
+
),
|
|
64
|
+
start,
|
|
65
|
+
end,
|
|
66
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
67
|
+
)
|
|
68
|
+
builder.setSpan(
|
|
69
|
+
MentionChipSpan(
|
|
70
|
+
backgroundColor = mentionStyle.backgroundColor,
|
|
71
|
+
textColor = mentionStyle.color,
|
|
72
|
+
paddingX = PixelUtil.toPixelFromDIP(PADDING_X_DP),
|
|
73
|
+
paddingY = PixelUtil.toPixelFromDIP(PADDING_Y_DP),
|
|
74
|
+
),
|
|
75
|
+
start,
|
|
76
|
+
end,
|
|
77
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
companion object {
|
|
82
|
+
private const val PADDING_X_DP = 3f
|
|
83
|
+
private const val PADDING_Y_DP = 1f
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
package com.renderrichtext.renderer
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.text.SpannableStringBuilder
|
|
5
|
+
import android.text.Spanned
|
|
6
|
+
import android.text.style.MetricAffectingSpan
|
|
7
|
+
import com.renderrichtext.math.LatexErrorReporter
|
|
8
|
+
import com.renderrichtext.parser.TiptapASTNode
|
|
9
|
+
import com.renderrichtext.spans.ImageSpan
|
|
10
|
+
import com.renderrichtext.styles.StyleConfig
|
|
11
|
+
import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
12
|
+
|
|
13
|
+
interface NodeRenderer {
|
|
14
|
+
fun render(
|
|
15
|
+
node: TiptapASTNode,
|
|
16
|
+
builder: SpannableStringBuilder,
|
|
17
|
+
onLinkPress: ((String) -> Unit)?,
|
|
18
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
19
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
20
|
+
factory: RendererFactory,
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
data class RendererConfig(
|
|
25
|
+
val style: StyleConfig,
|
|
26
|
+
val onLatexError: LatexErrorReporter? = null,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
class RendererFactory(
|
|
30
|
+
private val config: RendererConfig,
|
|
31
|
+
val context: Context,
|
|
32
|
+
private val onImageSpanCreated: (ImageSpan) -> Unit,
|
|
33
|
+
) {
|
|
34
|
+
val blockStyleContext = BlockStyleContext()
|
|
35
|
+
|
|
36
|
+
val styleCache = SpanStyleCache(config.style)
|
|
37
|
+
|
|
38
|
+
/** Set for the duration of one render. Mention spans capture the values. */
|
|
39
|
+
var onMentionPress: ((String, String) -> Unit)? = null
|
|
40
|
+
var onMentionLongPress: ((String, String) -> Unit)? = null
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Spans registered here are applied after the full document tree is rendered, so they
|
|
44
|
+
* always end up after block-level spans in the SpannableStringBuilder's internal array.
|
|
45
|
+
* See [BaselineShiftRenderer] for the root cause and the proper long-term fix.
|
|
46
|
+
*
|
|
47
|
+
* The range is kept as offsets. A renderer that inserts text into the builder after
|
|
48
|
+
* its children rendered (the blockquote padding spacer) must report it through
|
|
49
|
+
* [shiftDeferredSpans], so the recorded ranges keep following their text.
|
|
50
|
+
*/
|
|
51
|
+
private class DeferredSpan(
|
|
52
|
+
val span: MetricAffectingSpan,
|
|
53
|
+
var start: Int,
|
|
54
|
+
var end: Int,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
private val deferredSpans = mutableListOf<DeferredSpan>()
|
|
58
|
+
private val afterRenderActions = mutableListOf<(Spanned) -> Unit>()
|
|
59
|
+
|
|
60
|
+
fun registerDeferredSpan(
|
|
61
|
+
span: MetricAffectingSpan,
|
|
62
|
+
start: Int,
|
|
63
|
+
end: Int,
|
|
64
|
+
) {
|
|
65
|
+
deferredSpans.add(DeferredSpan(span, start, end))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Runs [action] on the finished text, once every block-level span is in place. */
|
|
69
|
+
fun runAfterRender(action: (Spanned) -> Unit) {
|
|
70
|
+
afterRenderActions.add(action)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Call right after inserting [length] characters at [position] into the builder being rendered. */
|
|
74
|
+
fun shiftDeferredSpans(
|
|
75
|
+
position: Int,
|
|
76
|
+
length: Int,
|
|
77
|
+
) {
|
|
78
|
+
// Spans are registered in document order, so only the most recent ones can lie at
|
|
79
|
+
// or after the insertion point.
|
|
80
|
+
for (index in deferredSpans.indices.reversed()) {
|
|
81
|
+
val deferred = deferredSpans[index]
|
|
82
|
+
if (deferred.end <= position) break
|
|
83
|
+
if (deferred.start >= position) deferred.start += length
|
|
84
|
+
deferred.end += length
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fun flushDeferredSpans(builder: SpannableStringBuilder) {
|
|
89
|
+
for (deferred in deferredSpans) {
|
|
90
|
+
builder.setSpan(deferred.span, deferred.start, deferred.end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
91
|
+
}
|
|
92
|
+
deferredSpans.clear()
|
|
93
|
+
afterRenderActions.forEach { it(builder) }
|
|
94
|
+
afterRenderActions.clear()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fun resetForNewRender() {
|
|
98
|
+
blockStyleContext.resetForNewRender()
|
|
99
|
+
deferredSpans.clear()
|
|
100
|
+
afterRenderActions.clear()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fun createImageSpan(
|
|
104
|
+
imageUrl: String,
|
|
105
|
+
isInline: Boolean,
|
|
106
|
+
altText: String,
|
|
107
|
+
): ImageSpan =
|
|
108
|
+
ImageSpan(
|
|
109
|
+
context = context,
|
|
110
|
+
imageUrl = imageUrl,
|
|
111
|
+
styleConfig = config.style,
|
|
112
|
+
isInline = isInline,
|
|
113
|
+
altText = altText,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
private val textRenderer = TextRenderer()
|
|
117
|
+
private val lineBreakRenderer = LineBreakRenderer()
|
|
118
|
+
private val softBreakRenderer = SoftBreakRenderer()
|
|
119
|
+
|
|
120
|
+
private val renderers: Map<TiptapASTNode.NodeType, NodeRenderer> by lazy {
|
|
121
|
+
buildMap {
|
|
122
|
+
put(TiptapASTNode.NodeType.Paragraph, ParagraphRenderer(config))
|
|
123
|
+
put(TiptapASTNode.NodeType.Heading, HeadingRenderer(config))
|
|
124
|
+
put(TiptapASTNode.NodeType.Blockquote, BlockquoteRenderer(config))
|
|
125
|
+
// A list-nested admonition falls back to the inline blockquote renderer
|
|
126
|
+
// (no themed header); top-level admonitions use the segment container path.
|
|
127
|
+
put(TiptapASTNode.NodeType.Admonition, BlockquoteRenderer(config))
|
|
128
|
+
put(TiptapASTNode.NodeType.CodeBlock, CodeBlockRenderer(config))
|
|
129
|
+
put(TiptapASTNode.NodeType.UnorderedList, ListRenderer(config, isOrdered = false))
|
|
130
|
+
put(TiptapASTNode.NodeType.OrderedList, ListRenderer(config, isOrdered = true))
|
|
131
|
+
put(TiptapASTNode.NodeType.ListItem, ListItemRenderer(config))
|
|
132
|
+
put(TiptapASTNode.NodeType.Text, textRenderer)
|
|
133
|
+
put(TiptapASTNode.NodeType.Link, LinkRenderer(config))
|
|
134
|
+
put(TiptapASTNode.NodeType.Strong, StrongRenderer(config))
|
|
135
|
+
put(TiptapASTNode.NodeType.Emphasis, EmphasisRenderer(config))
|
|
136
|
+
put(TiptapASTNode.NodeType.Strikethrough, StrikethroughRenderer(config))
|
|
137
|
+
put(TiptapASTNode.NodeType.Underline, UnderlineRenderer(config))
|
|
138
|
+
put(TiptapASTNode.NodeType.Highlight, HighlightRenderer())
|
|
139
|
+
put(TiptapASTNode.NodeType.Code, CodeRenderer(config))
|
|
140
|
+
put(TiptapASTNode.NodeType.Factoid, FactoidRenderer(config))
|
|
141
|
+
put(TiptapASTNode.NodeType.Mention, MentionRenderer(config))
|
|
142
|
+
put(TiptapASTNode.NodeType.LineBreak, lineBreakRenderer)
|
|
143
|
+
put(TiptapASTNode.NodeType.SoftBreak, softBreakRenderer)
|
|
144
|
+
put(TiptapASTNode.NodeType.ThematicBreak, ThematicBreakRenderer(config))
|
|
145
|
+
put(TiptapASTNode.NodeType.BlankLine, BlankLineRenderer(config))
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Called by ImageRenderer to report a new span to the collector.
|
|
151
|
+
*/
|
|
152
|
+
fun registerImageSpan(span: ImageSpan) {
|
|
153
|
+
onImageSpanCreated(span)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fun getRenderer(node: TiptapASTNode): NodeRenderer =
|
|
157
|
+
renderers[node.type] ?: run {
|
|
158
|
+
android.util.Log.w("RendererFactory", "No renderer for: ${node.type}")
|
|
159
|
+
textRenderer
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fun renderChildren(
|
|
163
|
+
node: TiptapASTNode,
|
|
164
|
+
builder: SpannableStringBuilder,
|
|
165
|
+
onLinkPress: ((String) -> Unit)?,
|
|
166
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
167
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
168
|
+
) {
|
|
169
|
+
renderNodes(node.children, builder, onLinkPress, onLinkLongPress, onFactoidPress)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Renders a flat list of sibling nodes in order, dispatching each to its
|
|
174
|
+
* NodeRenderer. Renders a block's own child content directly - e.g. a GFM
|
|
175
|
+
* blockquote's content once its nested block segments have been split out -
|
|
176
|
+
* without wrapping it in a synthetic root node.
|
|
177
|
+
*/
|
|
178
|
+
fun renderNodes(
|
|
179
|
+
nodes: List<TiptapASTNode>,
|
|
180
|
+
builder: SpannableStringBuilder,
|
|
181
|
+
onLinkPress: ((String) -> Unit)?,
|
|
182
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
183
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
184
|
+
) {
|
|
185
|
+
nodes.forEach { node ->
|
|
186
|
+
getRenderer(node).render(node, builder, onLinkPress, onLinkLongPress, onFactoidPress, this)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Improved helper for applying spans to blocks of text.
|
|
192
|
+
*/
|
|
193
|
+
inline fun renderWithSpan(
|
|
194
|
+
builder: SpannableStringBuilder,
|
|
195
|
+
renderContent: () -> Unit,
|
|
196
|
+
applySpan: (start: Int, end: Int, blockStyle: BlockStyle) -> Unit,
|
|
197
|
+
) {
|
|
198
|
+
val start = builder.length
|
|
199
|
+
renderContent()
|
|
200
|
+
val end = builder.length
|
|
201
|
+
|
|
202
|
+
if (end > start) {
|
|
203
|
+
val blockStyle = blockStyleContext.requireBlockStyle()
|
|
204
|
+
applySpan(start, end, blockStyle)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
package com.renderrichtext.renderer
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableStringBuilder
|
|
4
|
+
import android.text.style.AlignmentSpan
|
|
5
|
+
import com.renderrichtext.parser.TiptapASTNode
|
|
6
|
+
import com.renderrichtext.styles.ParagraphStyle
|
|
7
|
+
import com.renderrichtext.utils.text.extensions.containsBlockImage
|
|
8
|
+
import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
9
|
+
import com.renderrichtext.utils.text.span.applyLineHeightSkippingImages
|
|
10
|
+
import com.renderrichtext.utils.text.span.applyMarginBottom
|
|
11
|
+
import com.renderrichtext.utils.text.span.applyMarginTop
|
|
12
|
+
|
|
13
|
+
class ParagraphRenderer(
|
|
14
|
+
private val config: RendererConfig,
|
|
15
|
+
) : NodeRenderer {
|
|
16
|
+
override fun render(
|
|
17
|
+
node: TiptapASTNode,
|
|
18
|
+
builder: SpannableStringBuilder,
|
|
19
|
+
onLinkPress: ((String) -> Unit)?,
|
|
20
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
21
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
22
|
+
factory: RendererFactory,
|
|
23
|
+
) {
|
|
24
|
+
val context = factory.blockStyleContext
|
|
25
|
+
|
|
26
|
+
// Inside a list, render tight with a single newline - lists own their item spacing.
|
|
27
|
+
if (context.listDepth > 0) {
|
|
28
|
+
factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
|
|
29
|
+
builder.append("\n")
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
val start = builder.length
|
|
34
|
+
val style = config.style.paragraphStyle
|
|
35
|
+
|
|
36
|
+
// Inside a blockquote, keep the quote's block style (font/color) and line height
|
|
37
|
+
// - applied by BlockquoteTextRenderer.postProcess - but still emit block margins so
|
|
38
|
+
// spacing between blocks matches the top-level document (a heading defaults to
|
|
39
|
+
// marginTop 0, so the gap before it comes from the preceding paragraph's marginBottom).
|
|
40
|
+
if (context.blockquoteDepth > 0) {
|
|
41
|
+
factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
|
|
42
|
+
if (builder.length > start) {
|
|
43
|
+
val marginTop = if (node.containsBlockImage()) config.style.imageStyle.marginTop else style.marginTop
|
|
44
|
+
applyMarginTop(builder, start, marginTop)
|
|
45
|
+
val marginBottom = if (node.containsBlockImage()) config.style.imageStyle.marginBottom else style.marginBottom
|
|
46
|
+
applyMarginBottom(builder, marginBottom)
|
|
47
|
+
}
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
context.setParagraphStyle(style)
|
|
52
|
+
try {
|
|
53
|
+
factory.renderChildren(node, builder, onLinkPress, onLinkLongPress, onFactoidPress)
|
|
54
|
+
} finally {
|
|
55
|
+
context.popBlockStyle()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (builder.length > start) {
|
|
59
|
+
builder.applySpans(node, style, start)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private fun SpannableStringBuilder.applySpans(
|
|
64
|
+
node: TiptapASTNode,
|
|
65
|
+
style: ParagraphStyle,
|
|
66
|
+
start: Int,
|
|
67
|
+
) {
|
|
68
|
+
val end = length
|
|
69
|
+
|
|
70
|
+
// TODO: LineHeightSpan may also clip superscript/subscript glyphs that extend
|
|
71
|
+
// outside the normal line bounds. A similar "skip" strategy as for images may
|
|
72
|
+
// be needed here once super/subscript usage in practice is better understood.
|
|
73
|
+
applyLineHeightSkippingImages(this, start, end, style.lineHeight)
|
|
74
|
+
|
|
75
|
+
// Only apply AlignmentSpan for non-default alignments (Center/Right)
|
|
76
|
+
if (style.textAlign.needsAlignmentSpan) {
|
|
77
|
+
setSpan(
|
|
78
|
+
AlignmentSpan.Standard(style.textAlign.layoutAlignment),
|
|
79
|
+
start,
|
|
80
|
+
end,
|
|
81
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
val marginTop = if (node.containsBlockImage()) config.style.imageStyle.marginTop else style.marginTop
|
|
86
|
+
applyMarginTop(this, start, marginTop)
|
|
87
|
+
|
|
88
|
+
val marginBottom = if (node.containsBlockImage()) config.style.imageStyle.marginBottom else style.marginBottom
|
|
89
|
+
applyMarginBottom(this, marginBottom)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
package com.renderrichtext.renderer
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.text.SpannableStringBuilder
|
|
5
|
+
import com.renderrichtext.math.LatexErrorReporter
|
|
6
|
+
import com.renderrichtext.parser.TiptapASTNode
|
|
7
|
+
import com.renderrichtext.spans.ImageSpan
|
|
8
|
+
import com.renderrichtext.spans.MarginBottomSpan
|
|
9
|
+
import com.renderrichtext.styles.StyleConfig
|
|
10
|
+
|
|
11
|
+
class Renderer {
|
|
12
|
+
private var cachedFactory: RendererFactory? = null
|
|
13
|
+
private var cachedStyle: StyleConfig? = null
|
|
14
|
+
private var cachedContext: Context? = null
|
|
15
|
+
private var cachedOnLatexError: LatexErrorReporter? = null
|
|
16
|
+
|
|
17
|
+
private val collectedImageSpans = mutableListOf<ImageSpan>()
|
|
18
|
+
private var lastElementMarginBottom: Float = 0f
|
|
19
|
+
|
|
20
|
+
fun configure(
|
|
21
|
+
style: StyleConfig,
|
|
22
|
+
context: Context,
|
|
23
|
+
onLatexError: LatexErrorReporter? = null,
|
|
24
|
+
) {
|
|
25
|
+
if (cachedStyle === style && cachedContext === context && cachedOnLatexError === onLatexError) return
|
|
26
|
+
|
|
27
|
+
cachedStyle = style
|
|
28
|
+
cachedContext = context
|
|
29
|
+
cachedOnLatexError = onLatexError
|
|
30
|
+
cachedFactory =
|
|
31
|
+
RendererFactory(
|
|
32
|
+
RendererConfig(style, onLatexError),
|
|
33
|
+
context,
|
|
34
|
+
) { span -> reportImageSpan(span) }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
fun renderDocument(
|
|
38
|
+
document: TiptapASTNode,
|
|
39
|
+
onLinkPress: ((String) -> Unit)? = null,
|
|
40
|
+
onLinkLongPress: ((String) -> Unit)? = null,
|
|
41
|
+
onFactoidPress: ((String) -> Unit)? = null,
|
|
42
|
+
onMentionPress: ((String, String) -> Unit)? = null,
|
|
43
|
+
onMentionLongPress: ((String, String) -> Unit)? = null,
|
|
44
|
+
): SpannableStringBuilder =
|
|
45
|
+
renderContent(
|
|
46
|
+
document.children,
|
|
47
|
+
onLinkPress,
|
|
48
|
+
onLinkLongPress,
|
|
49
|
+
onFactoidPress,
|
|
50
|
+
onMentionPress,
|
|
51
|
+
onMentionLongPress,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Renders a flat list of sibling nodes into a fresh buffer and hands over
|
|
56
|
+
* ownership of it. This is the generic envelope shared by the document and
|
|
57
|
+
* block-segment paths: reset the factory, build the spannable, trim the trailing
|
|
58
|
+
* margin, and flush deferred spans (e.g. BaselineShiftSpan) after all block-level
|
|
59
|
+
* spans are set - see BaselineShiftRenderer for the proper long-term fix.
|
|
60
|
+
*
|
|
61
|
+
* An optional [block] decorates the pass - it enters a block style before the
|
|
62
|
+
* nodes render and post-processes the finished builder - letting a caller render
|
|
63
|
+
* content as e.g. blockquote text without this class knowing anything
|
|
64
|
+
* block-specific. See [BlockquoteTextRenderer].
|
|
65
|
+
*
|
|
66
|
+
* The builder is returned as-is rather than frozen into a `SpannableString`:
|
|
67
|
+
* the tiptap text view adopts it verbatim (see `NoCopySpannableFactory`),
|
|
68
|
+
* and converting it would cost a quadratic span copy for no benefit. Each call
|
|
69
|
+
* builds its own buffer, so callers must not mutate one they have passed on.
|
|
70
|
+
*/
|
|
71
|
+
fun renderContent(
|
|
72
|
+
nodes: List<TiptapASTNode>,
|
|
73
|
+
onLinkPress: ((String) -> Unit)? = null,
|
|
74
|
+
onLinkLongPress: ((String) -> Unit)? = null,
|
|
75
|
+
onFactoidPress: ((String) -> Unit)? = null,
|
|
76
|
+
onMentionPress: ((String, String) -> Unit)? = null,
|
|
77
|
+
onMentionLongPress: ((String, String) -> Unit)? = null,
|
|
78
|
+
block: BlockquoteTextRenderer? = null,
|
|
79
|
+
): SpannableStringBuilder {
|
|
80
|
+
val factory =
|
|
81
|
+
requireNotNull(cachedFactory) {
|
|
82
|
+
"Renderer must be configured with a style before rendering."
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
factory.resetForNewRender()
|
|
86
|
+
factory.onMentionPress = onMentionPress
|
|
87
|
+
factory.onMentionLongPress = onMentionLongPress
|
|
88
|
+
collectedImageSpans.clear()
|
|
89
|
+
lastElementMarginBottom = 0f
|
|
90
|
+
|
|
91
|
+
val builder = SpannableStringBuilder()
|
|
92
|
+
|
|
93
|
+
block?.push(factory.blockStyleContext)
|
|
94
|
+
try {
|
|
95
|
+
factory.renderNodes(nodes, builder, onLinkPress, onLinkLongPress, onFactoidPress)
|
|
96
|
+
} finally {
|
|
97
|
+
block?.pop(factory.blockStyleContext)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
removeTrailingMargin(builder)
|
|
101
|
+
block?.postProcess(builder)
|
|
102
|
+
factory.flushDeferredSpans(builder)
|
|
103
|
+
|
|
104
|
+
return builder
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Removes trailing newlines and captures the margin of the final element. */
|
|
108
|
+
private fun removeTrailingMargin(builder: SpannableStringBuilder) {
|
|
109
|
+
if (builder.isEmpty()) return
|
|
110
|
+
|
|
111
|
+
// Identify the last margin span and store its value
|
|
112
|
+
val lastSpan =
|
|
113
|
+
builder
|
|
114
|
+
.getSpans(0, builder.length, MarginBottomSpan::class.java)
|
|
115
|
+
.maxByOrNull { builder.getSpanEnd(it) }
|
|
116
|
+
|
|
117
|
+
lastElementMarginBottom = lastSpan?.marginBottom ?: 0f
|
|
118
|
+
|
|
119
|
+
// Trim trailing newlines
|
|
120
|
+
while (builder.endsWith('\n')) {
|
|
121
|
+
builder.delete(builder.length - 1, builder.length)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Clean up the span if it no longer covers any text
|
|
125
|
+
if (lastSpan != null && builder.getSpanEnd(lastSpan) >= builder.length) {
|
|
126
|
+
builder.removeSpan(lastSpan)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns the marginBottom value of the last element in the document.
|
|
132
|
+
* This is dynamically determined from the actual last element (paragraph, image, heading, etc.)
|
|
133
|
+
* and can be used in MeasurementStore to adjust the measured height.
|
|
134
|
+
*/
|
|
135
|
+
fun getLastElementMarginBottom(): Float = lastElementMarginBottom
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Internal helper used by the Factory's lambda to collect spans.
|
|
139
|
+
*/
|
|
140
|
+
private fun reportImageSpan(span: ImageSpan) {
|
|
141
|
+
collectedImageSpans.add(span)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Provides the EnrichedTiptapText with the exact list of spans that need registration.
|
|
146
|
+
*/
|
|
147
|
+
fun getCollectedImageSpans(): List<ImageSpan> = collectedImageSpans
|
|
148
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package com.renderrichtext.renderer
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableStringBuilder
|
|
4
|
+
import com.renderrichtext.parser.TiptapASTNode
|
|
5
|
+
|
|
6
|
+
class SoftBreakRenderer : NodeRenderer {
|
|
7
|
+
override fun render(
|
|
8
|
+
node: TiptapASTNode,
|
|
9
|
+
builder: SpannableStringBuilder,
|
|
10
|
+
onLinkPress: ((String) -> Unit)?,
|
|
11
|
+
onLinkLongPress: ((String) -> Unit)?,
|
|
12
|
+
onFactoidPress: ((String) -> Unit)?,
|
|
13
|
+
factory: RendererFactory,
|
|
14
|
+
) {
|
|
15
|
+
builder.append(" ")
|
|
16
|
+
}
|
|
17
|
+
}
|