@apollohg/react-native-rich-text-editor 2.0.0
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 +160 -0
- package/README.md +259 -0
- package/RUST-STANDARD-LIBRARY-NOTICES.html +8266 -0
- package/ReactNativeProseEditor.podspec +59 -0
- package/THIRD_PARTY_NOTICES.md +31155 -0
- package/android/build.gradle +188 -0
- package/android/consumer-rules.pro +18 -0
- package/android/expo/build.gradle +17 -0
- package/android/src/debug/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +7 -0
- package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
- package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
- package/android/src/main/java/com/apollohg/editor/AndroidApiCompat.kt +19 -0
- package/android/src/main/java/com/apollohg/editor/AndroidCollaborationTransport.kt +860 -0
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +91 -0
- package/android/src/main/java/com/apollohg/editor/CodeHighlighting.kt +141 -0
- package/android/src/main/java/com/apollohg/editor/DecodedBitmapBudget.kt +250 -0
- package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +133 -0
- package/android/src/main/java/com/apollohg/editor/EditorBlockSpacing.kt +51 -0
- package/android/src/main/java/com/apollohg/editor/EditorBoxDrawable.kt +16 -0
- package/android/src/main/java/com/apollohg/editor/EditorBoxDrawing.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +87 -0
- package/android/src/main/java/com/apollohg/editor/EditorCodeHighlighting.kt +120 -0
- package/android/src/main/java/com/apollohg/editor/EditorDecorationLayer.java +18 -0
- package/android/src/main/java/com/apollohg/editor/EditorDocumentLayout.kt +626 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +1042 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextAnnotations.kt +191 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextCaret.kt +100 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextClipboard.kt +101 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextComposition.kt +220 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +316 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextCorrection.kt +303 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDeferredUpdates.kt +327 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDeletion.kt +258 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDiagnostics.kt +83 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextDragDrop.kt +67 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextExternalComposition.kt +357 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextHardwareInput.kt +277 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +280 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputLifecycle.kt +183 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputPreparation.kt +79 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextInputTraits.kt +384 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextLayout.kt +100 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextNativeMutation.kt +243 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextPlaceholder.kt +154 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRenderPatch.kt +347 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRendering.kt +313 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextRustCommands.kt +281 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextSelection.kt +352 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextState.kt +113 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextTaskMarkers.kt +92 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextTextCommit.kt +327 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +84 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextUpdates.kt +245 -0
- package/android/src/main/java/com/apollohg/editor/EditorGlyphHeightCursorDrawable.kt +42 -0
- package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +13 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +923 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCoordinates.kt +191 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCorrection.kt +107 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputCursorGeometry.kt +128 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputSnapshots.kt +134 -0
- package/android/src/main/java/com/apollohg/editor/EditorInputSurroundingDeletion.kt +321 -0
- package/android/src/main/java/com/apollohg/editor/EditorKeyboardToolbarView.kt +851 -0
- package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +192 -0
- package/android/src/main/java/com/apollohg/editor/EditorReconciliationWatcher.kt +46 -0
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +303 -0
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheetSpans.kt +253 -0
- package/android/src/main/java/com/apollohg/editor/EditorSurfaceInputConnection.kt +67 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextDecorationDrawing.kt +143 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurface.kt +712 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceDragDrop.kt +94 -0
- package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceInteraction.kt +708 -0
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +650 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +912 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterMutations.kt +402 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterRendering.kt +123 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2AdapterSelection.kt +203 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Backend.kt +120 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +87 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Registry.kt +97 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +409 -0
- package/android/src/main/java/com/apollohg/editor/ExpoAutoGrowStyleSizePublisher.kt +34 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +266 -0
- package/android/src/main/java/com/apollohg/editor/ImeTextCoordinateMapper.kt +102 -0
- package/android/src/main/java/com/apollohg/editor/KeyboardToolbarImeAnimationController.kt +101 -0
- package/android/src/main/java/com/apollohg/editor/MentionSuggestionChipView.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/NativeCollaborationTransportRegistry.kt +493 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +813 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewAtoms.kt +68 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewBinding.kt +126 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewDiagnostics.kt +208 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewErrors.kt +146 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewEvents.kt +150 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewExternalComposition.kt +39 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewFocus.kt +176 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewLifecycle.kt +302 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewMentions.kt +259 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewNativeActions.kt +179 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewOutsideTap.kt +214 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPendingUpdates.kt +376 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewProperties.kt +234 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPropertyRetries.kt +197 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewToolbar.kt +285 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewTypes.kt +28 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewUpdates.kt +271 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +687 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorOutsideTapDispatcher.kt +380 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorV2Numbers.kt +45 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorViewRegistry.kt +372 -0
- package/android/src/main/java/com/apollohg/editor/NativeImagePipeline.kt +22 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +193 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbarIcons.kt +177 -0
- package/android/src/main/java/com/apollohg/editor/NativeToolbarItem.kt +274 -0
- package/android/src/main/java/com/apollohg/editor/OkHttpCollaborationSocket.kt +102 -0
- package/android/src/main/java/com/apollohg/editor/OrderedListMarkerFormatter.kt +130 -0
- package/android/src/main/java/com/apollohg/editor/PointerTransparentView.java +18 -0
- package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +128 -0
- package/android/src/main/java/com/apollohg/editor/ProseViewerView.kt +854 -0
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +314 -0
- package/android/src/main/java/com/apollohg/editor/RenderBlockSpans.kt +314 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +224 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +407 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +544 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +631 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridgeParagraphs.kt +215 -0
- package/android/src/main/java/com/apollohg/editor/RenderImageDecoder.kt +748 -0
- package/android/src/main/java/com/apollohg/editor/RenderImageLoader.kt +900 -0
- package/android/src/main/java/com/apollohg/editor/RenderLayoutConstants.kt +117 -0
- package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +337 -0
- package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +182 -0
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +951 -0
- package/android/src/main/java/com/apollohg/editor/SharedNativeImagePipeline.kt +78 -0
- package/android/src/main/java/com/apollohg/editor/UniffiEditorV2Backend.kt +261 -0
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1656 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FabricAttachmentSidecars.kt +80 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FabricLeaseHandleBridge.kt +59 -0
- package/android/src/main/java/com/apollohg/editor/viewer/FallbackSelectionGeometry.kt +450 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseDrawingView.kt +678 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseInstrumentation.kt +608 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayout.kt +186 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutCache.kt +393 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutRegistry.kt +828 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseTheme.kt +316 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerManager.kt +900 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerPackage.kt +16 -0
- package/android/src/main/java/com/apollohg/editor/viewer/PreparedViewerAtoms.kt +89 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerCodeHighlighting.kt +91 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerDocument.kt +453 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerFontEnvironment.kt +282 -0
- package/android/src/main/java/com/apollohg/editor/viewer/ViewerImagePipeline.kt +460 -0
- package/android/src/main/jni/CMakeLists.txt +85 -0
- package/android/src/main/jni/PreparedProseViewerMeasurementsManager.cpp +334 -0
- package/android/src/main/jni/ReactNativeProseEditorSpec.h +17 -0
- package/android/src/main/jni/reactnativeproseeditor.h +6 -0
- package/android/src/main/res/values/attrs.xml +6 -0
- package/android/src/release/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +9 -0
- package/app.plugin.js +62 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseMeasurementsManager.h +62 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerComponentDescriptor.h +30 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.cpp +192 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.h +46 -0
- package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerState.h +145 -0
- package/dist/AtomHost.d.ts +17 -0
- package/dist/AtomHost.js +135 -0
- package/dist/DefaultAtomChip.d.ts +2 -0
- package/dist/DefaultAtomChip.js +22 -0
- package/dist/EditorAddon.d.ts +32 -0
- package/dist/EditorAddon.js +29 -0
- package/dist/EditorAddonNormalization.d.ts +6 -0
- package/dist/EditorAddonNormalization.js +48 -0
- package/dist/EditorMentionThemeNormalization.d.ts +8 -0
- package/dist/EditorMentionThemeNormalization.js +22 -0
- package/dist/EditorMentionThemeValidation.d.ts +12 -0
- package/dist/EditorMentionThemeValidation.js +95 -0
- package/dist/EditorStyleSheet.d.ts +4 -0
- package/dist/EditorStyleSheet.js +10 -0
- package/dist/EditorStyleSheetNormalization.d.ts +3 -0
- package/dist/EditorStyleSheetNormalization.js +226 -0
- package/dist/EditorStyleSheetTypes.d.ts +151 -0
- package/dist/EditorStyleSheetTypes.js +2 -0
- package/dist/EditorTheme.d.ts +117 -0
- package/dist/EditorTheme.js +41 -0
- package/dist/EditorToolbar.d.ts +4 -0
- package/dist/EditorToolbar.js +17 -0
- package/dist/EditorToolbarComponent.d.ts +13 -0
- package/dist/EditorToolbarComponent.js +24 -0
- package/dist/EditorToolbarItems.d.ts +9 -0
- package/dist/EditorToolbarItems.js +58 -0
- package/dist/EditorToolbarRegistry.d.ts +59 -0
- package/dist/EditorToolbarRegistry.js +175 -0
- package/dist/EditorToolbarTypes.d.ts +273 -0
- package/dist/EditorToolbarTypes.js +2 -0
- package/dist/EditorToolbarVisuals.d.ts +154 -0
- package/dist/EditorToolbarVisuals.js +228 -0
- package/dist/EditorUpdateRevision.d.ts +8 -0
- package/dist/EditorUpdateRevision.js +22 -0
- package/dist/ExternalTextComposition.d.ts +45 -0
- package/dist/ExternalTextComposition.js +537 -0
- package/dist/ImageLoadingPolicy.d.ts +54 -0
- package/dist/ImageLoadingPolicy.js +60 -0
- package/dist/NativeEditorBoundaryError.d.ts +111 -0
- package/dist/NativeEditorBoundaryError.js +268 -0
- package/dist/NativeEditorBridge.d.ts +9 -0
- package/dist/NativeEditorBridge.js +37 -0
- package/dist/NativeEditorCollaborationTransport.d.ts +154 -0
- package/dist/NativeEditorCollaborationTransport.js +275 -0
- package/dist/NativeEditorCreateConfig.d.ts +16 -0
- package/dist/NativeEditorCreateConfig.js +249 -0
- package/dist/NativeEditorCreateJson.d.ts +76 -0
- package/dist/NativeEditorCreateJson.js +474 -0
- package/dist/NativeEditorCreateValidation.d.ts +32 -0
- package/dist/NativeEditorCreateValidation.js +150 -0
- package/dist/NativeEditorDocumentBridge.d.ts +72 -0
- package/dist/NativeEditorDocumentBridge.js +305 -0
- package/dist/NativeEditorDocumentHandle.d.ts +70 -0
- package/dist/NativeEditorDocumentHandle.js +89 -0
- package/dist/NativeEditorLocalAwareness.d.ts +34 -0
- package/dist/NativeEditorLocalAwareness.js +153 -0
- package/dist/NativeEditorNativeModule.d.ts +48 -0
- package/dist/NativeEditorNativeModule.js +34 -0
- package/dist/NativeEditorRenderNormalization.d.ts +22 -0
- package/dist/NativeEditorRenderNormalization.js +274 -0
- package/dist/NativeEditorResultNormalization.d.ts +108 -0
- package/dist/NativeEditorResultNormalization.js +400 -0
- package/dist/NativeEditorTypes.d.ts +318 -0
- package/dist/NativeEditorTypes.js +15 -0
- package/dist/NativeEditorV2Decimal.d.ts +5 -0
- package/dist/NativeEditorV2Decimal.js +18 -0
- package/dist/NativeProseViewer.d.ts +122 -0
- package/dist/NativeProseViewer.js +130 -0
- package/dist/NativeRichTextEditor.d.ts +3 -0
- package/dist/NativeRichTextEditor.js +6 -0
- package/dist/ResourceLimits.d.ts +100 -0
- package/dist/ResourceLimits.js +80 -0
- package/dist/RichTextEditor.d.ts +4 -0
- package/dist/RichTextEditor.js +25 -0
- package/dist/RichTextEditorNativeTypes.d.ts +122 -0
- package/dist/RichTextEditorNativeTypes.js +2 -0
- package/dist/RichTextEditorNativeView.d.ts +12 -0
- package/dist/RichTextEditorNativeView.js +15 -0
- package/dist/RichTextEditorSerialization.d.ts +53 -0
- package/dist/RichTextEditorSerialization.js +283 -0
- package/dist/RichTextEditorTypes.d.ts +281 -0
- package/dist/RichTextEditorTypes.js +2 -0
- package/dist/ViewerConfiguration.d.ts +22 -0
- package/dist/ViewerConfiguration.js +6 -0
- package/dist/YjsCollaboration.d.ts +146 -0
- package/dist/YjsCollaboration.js +496 -0
- package/dist/addons.d.ts +193 -0
- package/dist/addons.js +122 -0
- package/dist/atomConstants.d.ts +3 -0
- package/dist/atomConstants.js +6 -0
- package/dist/atomInstances.d.ts +35 -0
- package/dist/atomInstances.js +79 -0
- package/dist/atomPolicy.d.ts +4 -0
- package/dist/atomPolicy.js +47 -0
- package/dist/atomUpdates.d.ts +2 -0
- package/dist/atomUpdates.js +38 -0
- package/dist/atoms.d.ts +92 -0
- package/dist/atoms.js +222 -0
- package/dist/attributeValidation.d.ts +4 -0
- package/dist/attributeValidation.js +97 -0
- package/dist/contentExpression.d.ts +6 -0
- package/dist/contentExpression.js +335 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +78 -0
- package/dist/listMarkers.d.ts +15 -0
- package/dist/listMarkers.js +21 -0
- package/dist/schemaDefinition.d.ts +143 -0
- package/dist/schemaDefinition.js +206 -0
- package/dist/schemaDocument.d.ts +17 -0
- package/dist/schemaDocument.js +69 -0
- package/dist/schemaDocumentConstruction.d.ts +5 -0
- package/dist/schemaDocumentConstruction.js +125 -0
- package/dist/schemaNormalization.d.ts +29 -0
- package/dist/schemaNormalization.js +313 -0
- package/dist/schemaPresets.d.ts +68 -0
- package/dist/schemaPresets.js +198 -0
- package/dist/schemaResolution.d.ts +9 -0
- package/dist/schemaResolution.js +211 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +28 -0
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +37 -0
- package/dist/specs/PreparedProseViewerNativeComponent.js +6 -0
- package/dist/useEditorToolbarInteractions.d.ts +12 -0
- package/dist/useEditorToolbarInteractions.js +218 -0
- package/dist/useEditorToolbarItems.d.ts +8 -0
- package/dist/useEditorToolbarItems.js +280 -0
- package/dist/useEditorToolbarPresentation.d.ts +4 -0
- package/dist/useEditorToolbarPresentation.js +203 -0
- package/dist/useEditorToolbarState.d.ts +63 -0
- package/dist/useEditorToolbarState.js +108 -0
- package/dist/useFocusPreservingFrames.d.ts +17 -0
- package/dist/useFocusPreservingFrames.js +108 -0
- package/dist/useNativeEditor.d.ts +66 -0
- package/dist/useNativeEditor.js +370 -0
- package/dist/useRichTextEditorCommands.d.ts +21 -0
- package/dist/useRichTextEditorCommands.js +299 -0
- package/dist/useRichTextEditorEvents.d.ts +18 -0
- package/dist/useRichTextEditorEvents.js +216 -0
- package/dist/useRichTextEditorMentions.d.ts +11 -0
- package/dist/useRichTextEditorMentions.js +316 -0
- package/dist/useRichTextEditorPresentation.d.ts +5 -0
- package/dist/useRichTextEditorPresentation.js +178 -0
- package/dist/useRichTextEditorState.d.ts +180 -0
- package/dist/useRichTextEditorState.js +405 -0
- package/dist/useRichTextEditorUpdates.d.ts +10 -0
- package/dist/useRichTextEditorUpdates.js +308 -0
- package/dist/useViewerAtoms.d.ts +38 -0
- package/dist/useViewerAtoms.js +288 -0
- package/expo-module.config.json +13 -0
- package/ios/EditorAddons.swift +354 -0
- package/ios/EditorCodeHighlighting.swift +184 -0
- package/ios/EditorCore.xcframework/Info.plist +44 -0
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorInteractionOverlays.swift +697 -0
- package/ios/EditorLayoutManager.swift +971 -0
- package/ios/EditorNodeTypes.swift +37 -0
- package/ios/EditorStyleSheet.swift +461 -0
- package/ios/EditorTextView+AttributedPatch.swift +305 -0
- package/ios/EditorTextView+Binding.swift +61 -0
- package/ios/EditorTextView+Caret.swift +333 -0
- package/ios/EditorTextView+Commands.swift +203 -0
- package/ios/EditorTextView+Delegate.swift +65 -0
- package/ios/EditorTextView+Diagnostics.swift +225 -0
- package/ios/EditorTextView+DragDrop.swift +290 -0
- package/ios/EditorTextView+ExternalComposition.swift +364 -0
- package/ios/EditorTextView+Images.swift +354 -0
- package/ios/EditorTextView+InputTraits.swift +200 -0
- package/ios/EditorTextView+KeyboardInput.swift +318 -0
- package/ios/EditorTextView+Layout.swift +245 -0
- package/ios/EditorTextView+MarkedText.swift +267 -0
- package/ios/EditorTextView+NativeInputLifecycle.swift +372 -0
- package/ios/EditorTextView+NativeMutation.swift +471 -0
- package/ios/EditorTextView+Reconciliation.swift +97 -0
- package/ios/EditorTextView+RenderPatch.swift +573 -0
- package/ios/EditorTextView+Rendering.swift +539 -0
- package/ios/EditorTextView+Selection.swift +542 -0
- package/ios/EditorTextView.swift +953 -0
- package/ios/EditorTheme.swift +626 -0
- package/ios/EditorV2Adapter+Commands.swift +204 -0
- package/ios/EditorV2Adapter+Envelope.swift +238 -0
- package/ios/EditorV2Adapter+Mutation.swift +392 -0
- package/ios/EditorV2Adapter+Registry.swift +185 -0
- package/ios/EditorV2Adapter+RenderState.swift +360 -0
- package/ios/EditorV2Adapter+RenderValidation.swift +420 -0
- package/ios/EditorV2Adapter+Selection.swift +329 -0
- package/ios/EditorV2Adapter+TextCommands.swift +430 -0
- package/ios/EditorV2Adapter.swift +376 -0
- package/ios/EditorV2Shadow.swift +353 -0
- package/ios/Generated_editor_core.swift +2378 -0
- package/ios/NativeCodeHighlighting.swift +159 -0
- package/ios/NativeCollaborationSocket.swift +144 -0
- package/ios/NativeCollaborationTransport+Protocol.swift +258 -0
- package/ios/NativeCollaborationTransport+Socket.swift +379 -0
- package/ios/NativeCollaborationTransport+Validation.swift +103 -0
- package/ios/NativeCollaborationTransport.swift +322 -0
- package/ios/NativeCollaborationTransportRegistry.swift +521 -0
- package/ios/NativeEditorExpoView+AccessoryRetry.swift +219 -0
- package/ios/NativeEditorExpoView+Binding.swift +268 -0
- package/ios/NativeEditorExpoView+Diagnostics.swift +66 -0
- package/ios/NativeEditorExpoView+Events.swift +131 -0
- package/ios/NativeEditorExpoView+Focus.swift +169 -0
- package/ios/NativeEditorExpoView+MentionRetry.swift +288 -0
- package/ios/NativeEditorExpoView+Mentions.swift +288 -0
- package/ios/NativeEditorExpoView+Properties.swift +300 -0
- package/ios/NativeEditorExpoView+Toolbar.swift +86 -0
- package/ios/NativeEditorExpoView+Updates.swift +312 -0
- package/ios/NativeEditorExpoView.swift +349 -0
- package/ios/NativeEditorModule.swift +856 -0
- package/ios/NativeEditorViewRegistry.swift +226 -0
- package/ios/NativeToolbar+ButtonStyle.swift +214 -0
- package/ios/NativeToolbar+Buttons.swift +228 -0
- package/ios/NativeToolbar+Diagnostics.swift +114 -0
- package/ios/NativeToolbar+Menu.swift +101 -0
- package/ios/NativeToolbar+Models.swift +519 -0
- package/ios/NativeToolbar+Placeholder.swift +39 -0
- package/ios/NativeToolbar.swift +612 -0
- package/ios/OrderedListMarkerFormatter.swift +103 -0
- package/ios/PositionBridge.swift +596 -0
- package/ios/ProseViewerView.swift +462 -0
- package/ios/RenderBridge+Atoms.swift +288 -0
- package/ios/RenderBridge+Attachments.swift +246 -0
- package/ios/RenderBridge+BlockBoundaries.swift +313 -0
- package/ios/RenderBridge+Style.swift +343 -0
- package/ios/RenderBridge.swift +704 -0
- package/ios/RichTextEditorView.swift +930 -0
- package/ios/SharedNativeImagePipeline+Cache.swift +189 -0
- package/ios/SharedNativeImagePipeline+Transport.swift +328 -0
- package/ios/SharedNativeImagePipeline.swift +533 -0
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +436 -0
- package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +322 -0
- package/ios/Viewer/CoreTextProseLayoutEngine.swift +635 -0
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.h +4 -0
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +767 -0
- package/ios/Viewer/Fabric/PreparedProseMeasurementsManager.mm +118 -0
- package/ios/Viewer/NativeSVGImageDecoder.h +9 -0
- package/ios/Viewer/NativeSVGImageDecoder.mm +106 -0
- package/ios/Viewer/NativeSVGPreflight.h +11 -0
- package/ios/Viewer/NativeSVGPreflight.mm +219 -0
- package/ios/Viewer/PreparedProseDrawingView.swift +639 -0
- package/ios/Viewer/PreparedProseInstrumentation.swift +387 -0
- package/ios/Viewer/PreparedProseLayout.swift +326 -0
- package/ios/Viewer/PreparedProseLayoutCache.swift +766 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+Artifacts.swift +377 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+Fabric.swift +582 -0
- package/ios/Viewer/PreparedProseLayoutRegistry+FabricOwnership.swift +128 -0
- package/ios/Viewer/PreparedProseLayoutRegistry.swift +381 -0
- package/ios/Viewer/ViewerDocument.swift +575 -0
- package/ios/Viewer/ViewerFontEnvironment.swift +283 -0
- package/ios/Viewer/ViewerImagePipeline.swift +478 -0
- package/ios/editor_coreFFI/editor_coreFFI.h +1023 -0
- package/ios/editor_coreFFI/module.modulemap +7 -0
- package/package.json +151 -0
- package/react-native.config.js +17 -0
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +3146 -0
- package/src/AtomHost.tsx +180 -0
- package/src/DefaultAtomChip.tsx +27 -0
- package/src/EditorAddon.ts +65 -0
- package/src/EditorAddonNormalization.ts +49 -0
- package/src/EditorMentionThemeNormalization.ts +25 -0
- package/src/EditorMentionThemeValidation.ts +133 -0
- package/src/EditorStyleSheet.ts +9 -0
- package/src/EditorStyleSheetNormalization.ts +219 -0
- package/src/EditorStyleSheetTypes.ts +189 -0
- package/src/EditorTheme.ts +196 -0
- package/src/EditorToolbar.tsx +31 -0
- package/src/EditorToolbarComponent.tsx +23 -0
- package/src/EditorToolbarItems.tsx +64 -0
- package/src/EditorToolbarRegistry.tsx +254 -0
- package/src/EditorToolbarTypes.tsx +322 -0
- package/src/EditorToolbarVisuals.tsx +270 -0
- package/src/EditorUpdateRevision.ts +26 -0
- package/src/ExternalTextComposition.ts +658 -0
- package/src/ImageLoadingPolicy.ts +125 -0
- package/src/NativeEditorBoundaryError.ts +345 -0
- package/src/NativeEditorBridge.ts +93 -0
- package/src/NativeEditorCollaborationTransport.ts +493 -0
- package/src/NativeEditorCreateConfig.ts +326 -0
- package/src/NativeEditorCreateJson.ts +579 -0
- package/src/NativeEditorCreateValidation.ts +178 -0
- package/src/NativeEditorDocumentBridge.ts +485 -0
- package/src/NativeEditorDocumentHandle.ts +168 -0
- package/src/NativeEditorLocalAwareness.ts +168 -0
- package/src/NativeEditorNativeModule.ts +97 -0
- package/src/NativeEditorRenderNormalization.ts +320 -0
- package/src/NativeEditorResultNormalization.ts +469 -0
- package/src/NativeEditorTypes.ts +366 -0
- package/src/NativeEditorV2Decimal.ts +13 -0
- package/src/NativeProseViewer.tsx +356 -0
- package/src/NativeRichTextEditor.tsx +33 -0
- package/src/ResourceLimits.ts +187 -0
- package/src/RichTextEditor.tsx +27 -0
- package/src/RichTextEditorNativeTypes.tsx +144 -0
- package/src/RichTextEditorNativeView.tsx +21 -0
- package/src/RichTextEditorSerialization.tsx +381 -0
- package/src/RichTextEditorTypes.tsx +327 -0
- package/src/ViewerConfiguration.ts +23 -0
- package/src/YjsCollaboration.ts +692 -0
- package/src/addons.ts +311 -0
- package/src/atomConstants.ts +3 -0
- package/src/atomInstances.ts +123 -0
- package/src/atomPolicy.ts +47 -0
- package/src/atomUpdates.ts +52 -0
- package/src/atoms.ts +379 -0
- package/src/attributeValidation.ts +116 -0
- package/src/contentExpression.ts +338 -0
- package/src/index.ts +295 -0
- package/src/listMarkers.ts +18 -0
- package/src/schemaDefinition.ts +395 -0
- package/src/schemaDocument.ts +92 -0
- package/src/schemaDocumentConstruction.ts +148 -0
- package/src/schemaNormalization.ts +367 -0
- package/src/schemaPresets.ts +250 -0
- package/src/schemaResolution.ts +242 -0
- package/src/schemas.ts +43 -0
- package/src/specs/PreparedProseViewerNativeComponent.ts +40 -0
- package/src/useEditorToolbarInteractions.tsx +329 -0
- package/src/useEditorToolbarItems.tsx +390 -0
- package/src/useEditorToolbarPresentation.tsx +483 -0
- package/src/useEditorToolbarState.tsx +172 -0
- package/src/useFocusPreservingFrames.ts +171 -0
- package/src/useNativeEditor.ts +534 -0
- package/src/useRichTextEditorCommands.tsx +478 -0
- package/src/useRichTextEditorEvents.tsx +342 -0
- package/src/useRichTextEditorMentions.tsx +441 -0
- package/src/useRichTextEditorPresentation.tsx +563 -0
- package/src/useRichTextEditorState.tsx +620 -0
- package/src/useRichTextEditorUpdates.tsx +403 -0
- package/src/useViewerAtoms.tsx +427 -0
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
import CryptoKit
|
|
2
|
+
import ImageIO
|
|
3
|
+
import UIKit
|
|
4
|
+
enum RenderBridgeAttributes {
|
|
5
|
+
/// Marks a character as a void element placeholder (hardBreak, horizontalRule).
|
|
6
|
+
/// The value is the node type string (e.g. "hardBreak", "horizontalRule").
|
|
7
|
+
static let voidNodeType = NSAttributedString.Key("com.apollohg.editor.voidNodeType")
|
|
8
|
+
|
|
9
|
+
/// Stores the Rust document position (UInt32) for void elements.
|
|
10
|
+
static let docPos = NSAttributedString.Key("com.apollohg.editor.docPos")
|
|
11
|
+
|
|
12
|
+
/// Marks a character as a block boundary (for block start/end tracking).
|
|
13
|
+
static let blockBoundary = NSAttributedString.Key("com.apollohg.editor.blockBoundary")
|
|
14
|
+
|
|
15
|
+
/// Stores the block node type (e.g. "paragraph", "listItem").
|
|
16
|
+
static let blockNodeType = NSAttributedString.Key("com.apollohg.editor.blockNodeType")
|
|
17
|
+
|
|
18
|
+
/// Stores the block depth (UInt8).
|
|
19
|
+
static let blockDepth = NSAttributedString.Key("com.apollohg.editor.blockDepth")
|
|
20
|
+
|
|
21
|
+
/// Stores list context info as a dictionary for list items.
|
|
22
|
+
static let listContext = NSAttributedString.Key("com.apollohg.editor.listContext")
|
|
23
|
+
|
|
24
|
+
/// Marks blocks that should render a visible list marker.
|
|
25
|
+
static let listMarkerContext = NSAttributedString.Key("com.apollohg.editor.listMarkerContext")
|
|
26
|
+
|
|
27
|
+
static let orderedListMarkerLabel = NSAttributedString.Key("com.apollohg.editor.orderedListMarkerLabel")
|
|
28
|
+
|
|
29
|
+
/// Stores the rendered list marker color for the paragraph marker.
|
|
30
|
+
static let listMarkerColor = NSAttributedString.Key("com.apollohg.editor.listMarkerColor")
|
|
31
|
+
|
|
32
|
+
/// Stores the rendered list marker scale for unordered bullets.
|
|
33
|
+
static let listMarkerScale = NSAttributedString.Key("com.apollohg.editor.listMarkerScale")
|
|
34
|
+
static let listMarkerGap = NSAttributedString.Key("com.apollohg.editor.listMarkerGap")
|
|
35
|
+
|
|
36
|
+
/// Stores the paragraph base font used to render the list marker.
|
|
37
|
+
static let listMarkerBaseFont = NSAttributedString.Key("com.apollohg.editor.listMarkerBaseFont")
|
|
38
|
+
|
|
39
|
+
/// Stores the reserved list marker gutter width.
|
|
40
|
+
static let listMarkerWidth = NSAttributedString.Key("com.apollohg.editor.listMarkerWidth")
|
|
41
|
+
|
|
42
|
+
/// Stores the rendered blockquote border color.
|
|
43
|
+
static let blockquoteBorderColor = NSAttributedString.Key("com.apollohg.editor.blockquoteBorderColor")
|
|
44
|
+
|
|
45
|
+
/// Stores the rendered blockquote border width.
|
|
46
|
+
static let blockquoteBorderWidth = NSAttributedString.Key("com.apollohg.editor.blockquoteBorderWidth")
|
|
47
|
+
|
|
48
|
+
/// Stores the rendered blockquote gap between border and text.
|
|
49
|
+
static let blockquoteMarkerGap = NSAttributedString.Key("com.apollohg.editor.blockquoteMarkerGap")
|
|
50
|
+
|
|
51
|
+
/// Marks code-block paragraphs for custom background drawing.
|
|
52
|
+
static let codeBlockBackgroundColor = NSAttributedString.Key("com.apollohg.editor.codeBlockBackgroundColor")
|
|
53
|
+
static let codeBlockBorderRadius = NSAttributedString.Key("com.apollohg.editor.codeBlockBorderRadius")
|
|
54
|
+
static let codeBlockPaddingHorizontal = NSAttributedString.Key("com.apollohg.editor.codeBlockPaddingHorizontal")
|
|
55
|
+
static let codeBlockPaddingVertical = NSAttributedString.Key("com.apollohg.editor.codeBlockPaddingVertical")
|
|
56
|
+
|
|
57
|
+
/// Marks synthetic zero-width placeholders used only for UIKit layout.
|
|
58
|
+
static let syntheticPlaceholder = NSAttributedString.Key("com.apollohg.editor.syntheticPlaceholder")
|
|
59
|
+
|
|
60
|
+
/// Stores the link href for visually styled link text without enabling UITextView's default link interaction.
|
|
61
|
+
static let linkHref = NSAttributedString.Key("com.apollohg.editor.linkHref")
|
|
62
|
+
|
|
63
|
+
/// Stores the owning top-level document child index for partial native patching.
|
|
64
|
+
static let topLevelChildIndex = NSAttributedString.Key("com.apollohg.editor.topLevelChildIndex")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Layout constants for paragraph styles.
|
|
68
|
+
enum LayoutConstants {
|
|
69
|
+
/// Spacing between paragraphs (points).
|
|
70
|
+
static let paragraphSpacing: CGFloat = 8.0
|
|
71
|
+
|
|
72
|
+
/// Base indentation per depth level (points).
|
|
73
|
+
static let indentPerDepth: CGFloat = 24.0
|
|
74
|
+
|
|
75
|
+
/// Width reserved for the list bullet/number (points).
|
|
76
|
+
static let listMarkerWidth: CGFloat = 36.0
|
|
77
|
+
|
|
78
|
+
/// Gap between the list marker and the text that follows (points).
|
|
79
|
+
static let listMarkerTextGap: CGFloat = 8.0
|
|
80
|
+
|
|
81
|
+
/// Height of the horizontal rule separator line (points).
|
|
82
|
+
static let horizontalRuleHeight: CGFloat = 1.0
|
|
83
|
+
|
|
84
|
+
/// Vertical padding above and below the horizontal rule (points).
|
|
85
|
+
static let horizontalRuleVerticalPadding: CGFloat = 8.0
|
|
86
|
+
|
|
87
|
+
/// Total leading inset reserved for each blockquote depth.
|
|
88
|
+
static let blockquoteIndent: CGFloat = 18.0
|
|
89
|
+
|
|
90
|
+
/// Width of the rendered blockquote border bar.
|
|
91
|
+
static let blockquoteBorderWidth: CGFloat = 3.0
|
|
92
|
+
|
|
93
|
+
/// Gap between the blockquote border bar and the text that follows.
|
|
94
|
+
static let blockquoteMarkerGap: CGFloat = 8.0
|
|
95
|
+
|
|
96
|
+
/// Bullet character for unordered list items.
|
|
97
|
+
static let unorderedListBullet = "\u{2022} "
|
|
98
|
+
|
|
99
|
+
/// Scale factor applied only to unordered list marker glyphs.
|
|
100
|
+
static let unorderedListMarkerFontScale: CGFloat = 2.0
|
|
101
|
+
|
|
102
|
+
/// Object replacement character used for void block elements.
|
|
103
|
+
static let objectReplacementCharacter = "\u{FFFC}"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
struct AtomRenderConfiguration: Equatable {
|
|
107
|
+
let registeredNodeTypes: Set<String>
|
|
108
|
+
let estimatedHeights: [String: CGFloat]
|
|
109
|
+
let measuredHeights: [String: CGFloat]
|
|
110
|
+
|
|
111
|
+
func reservedHeight(atomKey: String, nodeType: String) -> CGFloat? {
|
|
112
|
+
measuredHeights[atomKey] ?? estimatedHeights[nodeType]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static func from(json: String?) -> AtomRenderConfiguration? {
|
|
116
|
+
guard let json,
|
|
117
|
+
let data = json.data(using: .utf8),
|
|
118
|
+
let raw = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
119
|
+
else { return nil }
|
|
120
|
+
|
|
121
|
+
let nodeTypes = Set((raw["nodeTypes"] as? [Any] ?? []).compactMap { $0 as? String })
|
|
122
|
+
let rawHeights = raw["estimatedHeights"] as? [String: Any] ?? [:]
|
|
123
|
+
var estimatedHeights: [String: CGFloat] = [:]
|
|
124
|
+
for (nodeType, value) in rawHeights {
|
|
125
|
+
guard let number = value as? NSNumber else { continue }
|
|
126
|
+
let height = CGFloat(truncating: number)
|
|
127
|
+
guard height.isFinite, height >= 0 else { continue }
|
|
128
|
+
estimatedHeights[nodeType] = height
|
|
129
|
+
}
|
|
130
|
+
return AtomRenderConfiguration(
|
|
131
|
+
registeredNodeTypes: nodeTypes,
|
|
132
|
+
estimatedHeights: estimatedHeights,
|
|
133
|
+
measuredHeights: [:]
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// MARK: - RenderBridge
|
|
139
|
+
|
|
140
|
+
/// Converts RenderElement JSON (emitted by Rust editor-core via UniFFI) into
|
|
141
|
+
/// NSAttributedString for display in a UITextView.
|
|
142
|
+
///
|
|
143
|
+
/// The JSON format matches the output of `serialize_render_elements` in lib.rs:
|
|
144
|
+
/// ```json
|
|
145
|
+
/// [
|
|
146
|
+
/// {"type": "blockStart", "nodeType": "paragraph", "depth": 0},
|
|
147
|
+
/// {"type": "textRun", "text": "Hello ", "marks": []},
|
|
148
|
+
/// {"type": "textRun", "text": "world", "marks": ["bold"]},
|
|
149
|
+
/// {"type": "blockEnd"},
|
|
150
|
+
/// {"type": "voidInline", "nodeType": "hardBreak", "docPos": 12},
|
|
151
|
+
/// {"type": "voidBlock", "nodeType": "horizontalRule", "docPos": 15}
|
|
152
|
+
/// ]
|
|
153
|
+
/// ```
|
|
154
|
+
final class RenderBridge {
|
|
155
|
+
|
|
156
|
+
// MARK: - Public API
|
|
157
|
+
|
|
158
|
+
/// Convert a JSON array of RenderElements into an NSAttributedString.
|
|
159
|
+
///
|
|
160
|
+
/// - Parameters:
|
|
161
|
+
/// - json: A JSON string representing an array of render elements.
|
|
162
|
+
/// - baseFont: The default font for unstyled text.
|
|
163
|
+
/// - textColor: The default text color.
|
|
164
|
+
/// - Returns: The rendered attributed string. Returns an empty attributed
|
|
165
|
+
/// string if the JSON is invalid.
|
|
166
|
+
static func renderElements(
|
|
167
|
+
fromJSON json: String,
|
|
168
|
+
baseFont: UIFont,
|
|
169
|
+
textColor: UIColor,
|
|
170
|
+
theme: EditorTheme? = nil,
|
|
171
|
+
atomConfiguration: AtomRenderConfiguration? = nil
|
|
172
|
+
) -> NSAttributedString {
|
|
173
|
+
guard let data = json.data(using: .utf8),
|
|
174
|
+
let parsed = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]]
|
|
175
|
+
else {
|
|
176
|
+
return NSAttributedString()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return renderElements(
|
|
180
|
+
fromArray: parsed,
|
|
181
|
+
baseFont: baseFont,
|
|
182
|
+
textColor: textColor,
|
|
183
|
+
theme: theme,
|
|
184
|
+
atomConfiguration: atomConfiguration
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Convert a parsed array of RenderElement dictionaries into an NSAttributedString.
|
|
189
|
+
///
|
|
190
|
+
/// This is the main rendering entry point. It processes elements in order,
|
|
191
|
+
/// maintaining a block context stack for proper paragraph styling.
|
|
192
|
+
///
|
|
193
|
+
/// - Parameters:
|
|
194
|
+
/// - elements: Parsed JSON array where each element is a dictionary.
|
|
195
|
+
/// - baseFont: The default font for unstyled text.
|
|
196
|
+
/// - textColor: The default text color.
|
|
197
|
+
/// - Returns: The rendered attributed string.
|
|
198
|
+
static func renderElements(
|
|
199
|
+
fromArray elements: [[String: Any]],
|
|
200
|
+
baseFont: UIFont,
|
|
201
|
+
textColor: UIColor,
|
|
202
|
+
theme: EditorTheme? = nil,
|
|
203
|
+
atomConfiguration: AtomRenderConfiguration? = nil
|
|
204
|
+
) -> NSAttributedString {
|
|
205
|
+
let result = NSMutableAttributedString()
|
|
206
|
+
var blockStack: [BlockContext] = []
|
|
207
|
+
var isFirstBlock = true
|
|
208
|
+
var pendingTrailingParagraphSpacing: CGFloat?
|
|
209
|
+
var atomOccurrences: [String: Int] = [:]
|
|
210
|
+
|
|
211
|
+
for (elementIndex, element) in elements.enumerated() {
|
|
212
|
+
guard let type = element["type"] as? String else { continue }
|
|
213
|
+
let topLevelChildIndex = jsonInt(element["topLevelChildIndex"])
|
|
214
|
+
|
|
215
|
+
switch type {
|
|
216
|
+
case "textRun":
|
|
217
|
+
let text = element["text"] as? String ?? ""
|
|
218
|
+
let marks = element["marks"] as? [Any] ?? []
|
|
219
|
+
let isCodeBlock = blockStack.last?.nodeType == "codeBlock"
|
|
220
|
+
let blockFont = resolvedFont(
|
|
221
|
+
for: blockStack,
|
|
222
|
+
baseFont: baseFont,
|
|
223
|
+
theme: theme
|
|
224
|
+
)
|
|
225
|
+
let blockColor = resolvedTextColor(
|
|
226
|
+
for: blockStack,
|
|
227
|
+
textColor: textColor,
|
|
228
|
+
theme: theme
|
|
229
|
+
)
|
|
230
|
+
var baseAttrs = attributesForMarks(
|
|
231
|
+
marks,
|
|
232
|
+
baseFont: blockFont,
|
|
233
|
+
textColor: blockColor,
|
|
234
|
+
theme: theme
|
|
235
|
+
)
|
|
236
|
+
if let sheet = theme?.styleSheet {
|
|
237
|
+
var base = defaultAttributes(baseFont: blockFont, textColor: blockColor)
|
|
238
|
+
EditorStyleSheet.applyText(sheet.textValues(blockStack.last?.nodeType ?? "paragraph", ancestors: blockStack.dropLast().map(\.nodeType)), to: &base)
|
|
239
|
+
baseAttrs = sheet.inlineAttributes(marks, base: base)
|
|
240
|
+
}
|
|
241
|
+
if isCodeBlock {
|
|
242
|
+
// blockFont already carries theme.codeBlock.text. Keep an
|
|
243
|
+
// explicit code-block family for ordinary marked text;
|
|
244
|
+
// otherwise use the shared monospace resolver. In either
|
|
245
|
+
// case, the resolver accepts a face only if it satisfies
|
|
246
|
+
// the complete bold/italic request.
|
|
247
|
+
let resolvedFont = baseAttrs[.font] as? UIFont ?? blockFont
|
|
248
|
+
let markTraits = resolvedFont.fontDescriptor.symbolicTraits
|
|
249
|
+
.intersection([.traitBold, .traitItalic])
|
|
250
|
+
let themedFamily = theme?.codeBlock?.text?.fontFamily != nil
|
|
251
|
+
baseAttrs[.font] = ViewerFontEnvironment.shared.resolveFont(
|
|
252
|
+
family: themedFamily ? nil : "monospace",
|
|
253
|
+
size: resolvedFont.pointSize,
|
|
254
|
+
fallback: resolvedFont,
|
|
255
|
+
additionalTraits: markTraits,
|
|
256
|
+
semanticGeneration: "legacy-editor-theme"
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
let attrs = applyBlockStyle(
|
|
260
|
+
to: baseAttrs,
|
|
261
|
+
blockStack: blockStack,
|
|
262
|
+
theme: theme,
|
|
263
|
+
blockBaseFont: blockFont
|
|
264
|
+
)
|
|
265
|
+
let attributedText = NSAttributedString(string: text, attributes: attrs)
|
|
266
|
+
result.append(
|
|
267
|
+
attributedStringApplyingLeadingTopLevelChildIndexIfNeeded(
|
|
268
|
+
attributedText,
|
|
269
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
270
|
+
resultIsEmpty: result.length == 0
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
case "voidInline":
|
|
275
|
+
let nodeType = element["nodeType"] as? String ?? ""
|
|
276
|
+
guard let docPos = jsonUInt32(element["docPos"]) else { continue }
|
|
277
|
+
let attrs = element["attrs"] as? [String: Any] ?? [:]
|
|
278
|
+
if EditorNodeTypes.isHardBreak(nodeType) {
|
|
279
|
+
overrideTrailingParagraphSpacing(in: result, paragraphSpacing: 0)
|
|
280
|
+
}
|
|
281
|
+
let attrStr = attributedStringForVoidInline(
|
|
282
|
+
nodeType: nodeType,
|
|
283
|
+
docPos: docPos,
|
|
284
|
+
attrs: attrs,
|
|
285
|
+
baseFont: baseFont,
|
|
286
|
+
textColor: textColor,
|
|
287
|
+
blockStack: blockStack,
|
|
288
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
289
|
+
theme: theme
|
|
290
|
+
)
|
|
291
|
+
result.append(
|
|
292
|
+
attributedStringApplyingLeadingTopLevelChildIndexIfNeeded(
|
|
293
|
+
attrStr,
|
|
294
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
295
|
+
resultIsEmpty: result.length == 0
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
case "voidBlock":
|
|
300
|
+
let nodeType = element["nodeType"] as? String ?? ""
|
|
301
|
+
guard let docPos = jsonUInt32(element["docPos"]) else { continue }
|
|
302
|
+
let attrs = element["attrs"] as? [String: Any] ?? [:]
|
|
303
|
+
let occurrence = atomOccurrences[nodeType, default: 0]
|
|
304
|
+
atomOccurrences[nodeType] = occurrence + 1
|
|
305
|
+
let atomKey = (element["atomId"] as? String) ?? "\(nodeType):\(occurrence)"
|
|
306
|
+
|
|
307
|
+
if !isFirstBlock {
|
|
308
|
+
collapseTrailingSpacingBeforeHorizontalRuleIfNeeded(
|
|
309
|
+
in: result,
|
|
310
|
+
pendingParagraphSpacing: &pendingTrailingParagraphSpacing,
|
|
311
|
+
nodeType: nodeType,
|
|
312
|
+
theme: theme
|
|
313
|
+
)
|
|
314
|
+
applyPendingTrailingParagraphSpacing(
|
|
315
|
+
in: result,
|
|
316
|
+
pendingParagraphSpacing: &pendingTrailingParagraphSpacing
|
|
317
|
+
)
|
|
318
|
+
result.append(
|
|
319
|
+
interBlockNewline(
|
|
320
|
+
baseFont: baseFont,
|
|
321
|
+
textColor: textColor,
|
|
322
|
+
blockStack: [],
|
|
323
|
+
theme: theme,
|
|
324
|
+
topLevelChildIndex: topLevelChildIndex
|
|
325
|
+
)
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
isFirstBlock = false
|
|
329
|
+
|
|
330
|
+
let attrStr = attributedStringForVoidBlock(
|
|
331
|
+
nodeType: nodeType,
|
|
332
|
+
docPos: docPos,
|
|
333
|
+
elementAttrs: attrs,
|
|
334
|
+
baseFont: baseFont,
|
|
335
|
+
textColor: textColor,
|
|
336
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
337
|
+
theme: theme,
|
|
338
|
+
atomKey: atomKey,
|
|
339
|
+
atomConfiguration: atomConfiguration
|
|
340
|
+
)
|
|
341
|
+
if let sheet = theme?.styleSheet {
|
|
342
|
+
let styled = NSMutableAttributedString(attributedString: attrStr)
|
|
343
|
+
styled.addAttribute(editorStyledContentAttribute, value: true, range: NSRange(location: 0, length: styled.length))
|
|
344
|
+
let context = BlockContext(nodeType: nodeType, depth: blockStack.last?.depth ?? 0, listContext: nil)
|
|
345
|
+
let style = paragraphStyleForBlock(context, blockStack: blockStack + [context], theme: theme, baseFont: baseFont)
|
|
346
|
+
let inset = sheet.box(nodeType).inset
|
|
347
|
+
style.headIndent -= inset.left
|
|
348
|
+
style.firstLineHeadIndent -= inset.left
|
|
349
|
+
style.tailIndent += inset.right
|
|
350
|
+
style.paragraphSpacingBefore = sheet.box(nodeType).margin.top
|
|
351
|
+
style.paragraphSpacing = sheet.box(nodeType).margin.bottom
|
|
352
|
+
styled.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: styled.length))
|
|
353
|
+
styled.addAttribute(editorBlockSpacingBoxAttribute, value: EditorRenderedBox(box: sheet.box(nodeType), depth: blockStack.count, leading: 0, trailing: 0), range: NSRange(location: 0, length: styled.length))
|
|
354
|
+
result.append(styled)
|
|
355
|
+
} else { result.append(attrStr) }
|
|
356
|
+
pendingTrailingParagraphSpacing = theme?.effectiveTextStyle(
|
|
357
|
+
for: nodeType
|
|
358
|
+
).spacingAfter
|
|
359
|
+
|
|
360
|
+
case "opaqueInlineAtom":
|
|
361
|
+
let nodeType = element["nodeType"] as? String ?? ""
|
|
362
|
+
let label = element["label"] as? String ?? "?"
|
|
363
|
+
guard let docPos = jsonUInt32(element["docPos"]) else { continue }
|
|
364
|
+
let mentionTheme = (element["mentionTheme"] as? [String: Any]).map(
|
|
365
|
+
EditorMentionTheme.init(dictionary:)
|
|
366
|
+
)
|
|
367
|
+
let attrStr = attributedStringForOpaqueInlineAtom(
|
|
368
|
+
nodeType: nodeType,
|
|
369
|
+
label: label,
|
|
370
|
+
docPos: docPos,
|
|
371
|
+
baseFont: baseFont,
|
|
372
|
+
textColor: textColor,
|
|
373
|
+
blockStack: blockStack,
|
|
374
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
375
|
+
theme: theme,
|
|
376
|
+
mentionTheme: mentionTheme
|
|
377
|
+
)
|
|
378
|
+
result.append(
|
|
379
|
+
attributedStringApplyingLeadingTopLevelChildIndexIfNeeded(
|
|
380
|
+
attrStr,
|
|
381
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
382
|
+
resultIsEmpty: result.length == 0
|
|
383
|
+
)
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
case "opaqueBlockAtom":
|
|
387
|
+
let nodeType = element["nodeType"] as? String ?? ""
|
|
388
|
+
let label = element["label"] as? String ?? "?"
|
|
389
|
+
guard let docPos = jsonUInt32(element["docPos"]) else { continue }
|
|
390
|
+
|
|
391
|
+
if !isFirstBlock {
|
|
392
|
+
applyPendingTrailingParagraphSpacing(
|
|
393
|
+
in: result,
|
|
394
|
+
pendingParagraphSpacing: &pendingTrailingParagraphSpacing
|
|
395
|
+
)
|
|
396
|
+
result.append(
|
|
397
|
+
interBlockNewline(
|
|
398
|
+
baseFont: baseFont,
|
|
399
|
+
textColor: textColor,
|
|
400
|
+
blockStack: [],
|
|
401
|
+
theme: theme,
|
|
402
|
+
topLevelChildIndex: topLevelChildIndex
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
isFirstBlock = false
|
|
407
|
+
|
|
408
|
+
let attrStr = attributedStringForOpaqueBlockAtom(
|
|
409
|
+
nodeType: nodeType,
|
|
410
|
+
label: label,
|
|
411
|
+
docPos: docPos,
|
|
412
|
+
baseFont: baseFont,
|
|
413
|
+
textColor: textColor,
|
|
414
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
415
|
+
theme: theme
|
|
416
|
+
)
|
|
417
|
+
result.append(attrStr)
|
|
418
|
+
|
|
419
|
+
case "blockStart":
|
|
420
|
+
let nodeType = element["nodeType"] as? String ?? ""
|
|
421
|
+
let depth = jsonUInt8(element["depth"])
|
|
422
|
+
let listContext = element["listContext"] as? [String: Any]
|
|
423
|
+
let isListItemContainer = isListItemNodeType(nodeType) && listContext != nil
|
|
424
|
+
let isTransparentLayoutContainer = isTransparentContainer(nodeType)
|
|
425
|
+
var ctx = BlockContext(
|
|
426
|
+
nodeType: nodeType,
|
|
427
|
+
depth: depth,
|
|
428
|
+
listContext: listContext,
|
|
429
|
+
topLevelChildIndex: topLevelChildIndex,
|
|
430
|
+
markerPending: isListItemContainer,
|
|
431
|
+
language: element["language"] as? String
|
|
432
|
+
)
|
|
433
|
+
let nestedListItemContainer =
|
|
434
|
+
isListItemContainer && (theme?.list?.itemSpacing != nil)
|
|
435
|
+
&& blockStack.contains(where: {
|
|
436
|
+
isListItemNodeType($0.nodeType) && $0.listContext != nil
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
if !isListItemContainer && !isTransparentLayoutContainer {
|
|
440
|
+
if !isFirstBlock {
|
|
441
|
+
applyPendingTrailingParagraphSpacing(
|
|
442
|
+
in: result,
|
|
443
|
+
pendingParagraphSpacing: &pendingTrailingParagraphSpacing
|
|
444
|
+
)
|
|
445
|
+
let newlineBlockStack: [BlockContext]
|
|
446
|
+
if ctx.nodeType == "codeBlock" {
|
|
447
|
+
newlineBlockStack = []
|
|
448
|
+
} else if blockquoteDepth(in: blockStack + [ctx]) > 0,
|
|
449
|
+
!trailingRenderedContentHasBlockquote(in: result) {
|
|
450
|
+
newlineBlockStack = []
|
|
451
|
+
} else {
|
|
452
|
+
newlineBlockStack = blockStack + [ctx]
|
|
453
|
+
}
|
|
454
|
+
let collapsedSeparatorSpacing = collapsedParagraphSpacingAfterHorizontalRule(
|
|
455
|
+
in: result,
|
|
456
|
+
separatorBlockStack: newlineBlockStack,
|
|
457
|
+
theme: theme,
|
|
458
|
+
baseFont: baseFont
|
|
459
|
+
)
|
|
460
|
+
result.append(
|
|
461
|
+
interBlockNewline(
|
|
462
|
+
baseFont: baseFont,
|
|
463
|
+
textColor: textColor,
|
|
464
|
+
blockStack: newlineBlockStack,
|
|
465
|
+
theme: theme,
|
|
466
|
+
paragraphSpacingOverride: collapsedSeparatorSpacing,
|
|
467
|
+
topLevelChildIndex: topLevelChildIndex
|
|
468
|
+
)
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
isFirstBlock = false
|
|
472
|
+
} else if applyPendingTrailingParagraphSpacing(
|
|
473
|
+
in: result,
|
|
474
|
+
pendingParagraphSpacing: &pendingTrailingParagraphSpacing
|
|
475
|
+
) {
|
|
476
|
+
// Applied list item spacing queued when the previous item ended.
|
|
477
|
+
} else if nestedListItemContainer {
|
|
478
|
+
overrideTrailingParagraphSpacing(
|
|
479
|
+
in: result,
|
|
480
|
+
paragraphSpacing: CGFloat(theme?.list?.itemSpacing ?? 0)
|
|
481
|
+
)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if theme?.styleSheet != nil, isListItemContainer,
|
|
485
|
+
(listContext?["isFirst"] as? Bool) == true || (listContext?["index"] as? NSNumber)?.intValue == 1 {
|
|
486
|
+
let listName = (listContext?["kind"] as? String) == "task" ? "taskList" : ((listContext?["ordered"] as? Bool) == true ? "orderedList" : "bulletList")
|
|
487
|
+
blockStack.append(BlockContext(nodeType: listName, depth: depth, listContext: nil, styleStart: result.length))
|
|
488
|
+
}
|
|
489
|
+
ctx.styleStart = result.length
|
|
490
|
+
|
|
491
|
+
blockStack.append(ctx)
|
|
492
|
+
|
|
493
|
+
var markerListContext: [String: Any]?
|
|
494
|
+
if !isListItemContainer {
|
|
495
|
+
if let directListContext = listContext {
|
|
496
|
+
markerListContext = directListContext
|
|
497
|
+
} else {
|
|
498
|
+
markerListContext = consumePendingListMarker(from: &blockStack)
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if markerListContext != nil {
|
|
503
|
+
if var currentBlock = blockStack.popLast() {
|
|
504
|
+
currentBlock.listMarkerContext = markerListContext
|
|
505
|
+
if currentBlock.listContext != nil {
|
|
506
|
+
currentBlock.listContext = markerListContext
|
|
507
|
+
}
|
|
508
|
+
blockStack.append(currentBlock)
|
|
509
|
+
}
|
|
510
|
+
// On iOS we draw list markers outside the editable text stream so
|
|
511
|
+
// UIKit still sees paragraph-start for native capitalization.
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
case "blockEnd":
|
|
515
|
+
if let endedBlock = blockStack.popLast() {
|
|
516
|
+
appendTrailingLineBreakPlaceholderIfNeeded(
|
|
517
|
+
in: result,
|
|
518
|
+
endedBlock: endedBlock,
|
|
519
|
+
remainingBlockStack: blockStack,
|
|
520
|
+
baseFont: baseFont,
|
|
521
|
+
textColor: textColor,
|
|
522
|
+
theme: theme
|
|
523
|
+
)
|
|
524
|
+
let omitBottomMargin = endedBlock.nodeType == "paragraph"
|
|
525
|
+
&& blockStack.last?.nodeType == "blockquote"
|
|
526
|
+
&& elements.indices.contains(elementIndex + 1)
|
|
527
|
+
&& elements[elementIndex + 1]["type"] as? String == "blockEnd"
|
|
528
|
+
closeStyledBlock(endedBlock, ancestors: blockStack, in: result, theme: theme, baseFont: baseFont, textColor: textColor, omitBottomMargin: omitBottomMargin)
|
|
529
|
+
if EditorStyleSheet.element(endedBlock.nodeType) == "codeBlock", endedBlock.styleStart < result.length {
|
|
530
|
+
result.addAttribute(editorCodeBlockAttribute, value: EditorCodeBlockPresentation(language: endedBlock.language), range: NSRange(location: endedBlock.styleStart, length: result.length - endedBlock.styleStart))
|
|
531
|
+
}
|
|
532
|
+
if theme?.styleSheet != nil, endedBlock.listContext?["isLast"] as? Bool == true,
|
|
533
|
+
let container = blockStack.last,
|
|
534
|
+
["bulletList", "orderedList", "taskList"].contains(container.nodeType) {
|
|
535
|
+
blockStack.removeLast()
|
|
536
|
+
closeStyledBlock(container, ancestors: blockStack, in: result, theme: theme, baseFont: baseFont, textColor: textColor)
|
|
537
|
+
}
|
|
538
|
+
if endedBlock.listContext != nil, theme?.styleSheet == nil {
|
|
539
|
+
let spacing = (endedBlock.listContext?["isLast"] as? Bool) == true
|
|
540
|
+
? (theme?.list?.spacingAfter ?? theme?.list?.itemSpacing)
|
|
541
|
+
: theme?.list?.itemSpacing
|
|
542
|
+
if let spacing {
|
|
543
|
+
pendingTrailingParagraphSpacing = (pendingTrailingParagraphSpacing ?? 0) + spacing
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
default:
|
|
549
|
+
break
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if theme?.styleSheet != nil, result.length > 1 {
|
|
554
|
+
collapseStyledSiblingMargins(in: result)
|
|
555
|
+
}
|
|
556
|
+
return result
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
static func renderBlocks(
|
|
560
|
+
fromArray blocks: [[[String: Any]]],
|
|
561
|
+
startIndex: Int = 0,
|
|
562
|
+
includeLeadingInterBlockSeparator: Bool = false,
|
|
563
|
+
includeTrailingInterBlockSeparator: Bool = false,
|
|
564
|
+
baseFont: UIFont,
|
|
565
|
+
textColor: UIColor,
|
|
566
|
+
theme: EditorTheme? = nil,
|
|
567
|
+
atomConfiguration: AtomRenderConfiguration? = nil
|
|
568
|
+
) -> NSAttributedString {
|
|
569
|
+
var flattened: [[String: Any]] = []
|
|
570
|
+
flattened.reserveCapacity(blocks.reduce(0) { $0 + $1.count })
|
|
571
|
+
|
|
572
|
+
for (offset, block) in blocks.enumerated() {
|
|
573
|
+
let topLevelChildIndex = startIndex + offset
|
|
574
|
+
for element in block {
|
|
575
|
+
var tagged = element
|
|
576
|
+
tagged["topLevelChildIndex"] = topLevelChildIndex
|
|
577
|
+
flattened.append(tagged)
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
let renderedBlocks = renderElements(
|
|
582
|
+
fromArray: flattened,
|
|
583
|
+
baseFont: baseFont,
|
|
584
|
+
textColor: textColor,
|
|
585
|
+
theme: theme,
|
|
586
|
+
atomConfiguration: atomConfiguration
|
|
587
|
+
)
|
|
588
|
+
let needsLeadingInterBlockSeparator = includeLeadingInterBlockSeparator && startIndex > 0
|
|
589
|
+
guard !blocks.isEmpty,
|
|
590
|
+
needsLeadingInterBlockSeparator || includeTrailingInterBlockSeparator
|
|
591
|
+
else {
|
|
592
|
+
return renderedBlocks
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
let result = NSMutableAttributedString()
|
|
596
|
+
if needsLeadingInterBlockSeparator {
|
|
597
|
+
result.append(
|
|
598
|
+
interBlockNewline(
|
|
599
|
+
baseFont: baseFont,
|
|
600
|
+
textColor: textColor,
|
|
601
|
+
blockStack: [],
|
|
602
|
+
theme: theme,
|
|
603
|
+
topLevelChildIndex: startIndex
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
result.append(
|
|
607
|
+
removingLeadingTopLevelChildIndex(
|
|
608
|
+
from: renderedBlocks,
|
|
609
|
+
topLevelChildIndex: startIndex
|
|
610
|
+
)
|
|
611
|
+
)
|
|
612
|
+
} else {
|
|
613
|
+
result.append(renderedBlocks)
|
|
614
|
+
}
|
|
615
|
+
if includeTrailingInterBlockSeparator {
|
|
616
|
+
result.append(
|
|
617
|
+
interBlockNewline(
|
|
618
|
+
baseFont: baseFont,
|
|
619
|
+
textColor: textColor,
|
|
620
|
+
blockStack: [],
|
|
621
|
+
theme: theme,
|
|
622
|
+
topLevelChildIndex: startIndex + blocks.count
|
|
623
|
+
)
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
return result
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// MARK: - Height Pre-Measurement
|
|
630
|
+
|
|
631
|
+
static func measureHeight(
|
|
632
|
+
forRenderJSON renderJSON: String,
|
|
633
|
+
themeJSON: String?,
|
|
634
|
+
width: CGFloat
|
|
635
|
+
) -> CGFloat {
|
|
636
|
+
if !Thread.isMainThread {
|
|
637
|
+
return DispatchQueue.main.sync {
|
|
638
|
+
measureHeight(
|
|
639
|
+
forRenderJSON: renderJSON,
|
|
640
|
+
themeJSON: themeJSON,
|
|
641
|
+
width: width
|
|
642
|
+
)
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
guard width > 0 else { return 0 }
|
|
646
|
+
|
|
647
|
+
let theme = EditorTheme.from(json: themeJSON)
|
|
648
|
+
let baseFontSize = theme?.text?.fontSize ?? theme?.paragraph?.fontSize ?? 16
|
|
649
|
+
let baseFont = UIFont.systemFont(ofSize: baseFontSize)
|
|
650
|
+
let textColor = theme?.text?.color ?? UIColor.label
|
|
651
|
+
|
|
652
|
+
let attributedString = NativeImagePipeline.withoutLoading {
|
|
653
|
+
renderElements(
|
|
654
|
+
fromJSON: renderJSON,
|
|
655
|
+
baseFont: baseFont,
|
|
656
|
+
textColor: textColor,
|
|
657
|
+
theme: theme
|
|
658
|
+
)
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
guard attributedString.length > 0 else { return 0 }
|
|
662
|
+
|
|
663
|
+
let contentInsets = theme?.contentInsets
|
|
664
|
+
let topInset = contentInsets?.top ?? 0
|
|
665
|
+
let bottomInset = contentInsets?.bottom ?? 0
|
|
666
|
+
let leftInset = contentInsets?.left ?? 0
|
|
667
|
+
let rightInset = contentInsets?.right ?? 0
|
|
668
|
+
|
|
669
|
+
// When contentInsets are set, lineFragmentPadding is 0 (matches
|
|
670
|
+
// RichTextEditorView.theme didSet). Otherwise use the UITextView
|
|
671
|
+
// default of 5.
|
|
672
|
+
let lineFragmentPadding: CGFloat = contentInsets != nil ? 0 : 5
|
|
673
|
+
|
|
674
|
+
let textStorage = NSTextStorage(attributedString: attributedString)
|
|
675
|
+
let layoutManager = EditorLayoutManager()
|
|
676
|
+
let containerWidth = width - leftInset - rightInset - lineFragmentPadding * 2
|
|
677
|
+
let textContainer = NSTextContainer(
|
|
678
|
+
size: CGSize(width: max(containerWidth, 0), height: .greatestFiniteMagnitude)
|
|
679
|
+
)
|
|
680
|
+
textContainer.lineFragmentPadding = 0
|
|
681
|
+
|
|
682
|
+
layoutManager.addTextContainer(textContainer)
|
|
683
|
+
textStorage.addLayoutManager(layoutManager)
|
|
684
|
+
|
|
685
|
+
layoutManager.ensureLayout(for: textContainer)
|
|
686
|
+
|
|
687
|
+
var usedRect = layoutManager.usedRect(for: textContainer)
|
|
688
|
+
let extraLineFragmentRect = layoutManager.extraLineFragmentRect
|
|
689
|
+
if !extraLineFragmentRect.isEmpty {
|
|
690
|
+
usedRect = usedRect.union(extraLineFragmentRect)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
let height = ceil(usedRect.height + topInset + bottomInset)
|
|
694
|
+
return height
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// MARK: - Private Helpers
|
|
698
|
+
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// MARK: - BlockContext
|
|
702
|
+
|
|
703
|
+
/// Transient context while rendering block elements. Pushed onto a stack
|
|
704
|
+
/// when a `blockStart` element is encountered and popped on `blockEnd`.
|