@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.
Files changed (494) hide show
  1. package/LICENSE +160 -0
  2. package/README.md +259 -0
  3. package/RUST-STANDARD-LIBRARY-NOTICES.html +8266 -0
  4. package/ReactNativeProseEditor.podspec +59 -0
  5. package/THIRD_PARTY_NOTICES.md +31155 -0
  6. package/android/build.gradle +188 -0
  7. package/android/consumer-rules.pro +18 -0
  8. package/android/expo/build.gradle +17 -0
  9. package/android/src/debug/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +7 -0
  10. package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
  11. package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
  12. package/android/src/main/java/com/apollohg/editor/AndroidApiCompat.kt +19 -0
  13. package/android/src/main/java/com/apollohg/editor/AndroidCollaborationTransport.kt +860 -0
  14. package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +91 -0
  15. package/android/src/main/java/com/apollohg/editor/CodeHighlighting.kt +141 -0
  16. package/android/src/main/java/com/apollohg/editor/DecodedBitmapBudget.kt +250 -0
  17. package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +133 -0
  18. package/android/src/main/java/com/apollohg/editor/EditorBlockSpacing.kt +51 -0
  19. package/android/src/main/java/com/apollohg/editor/EditorBoxDrawable.kt +16 -0
  20. package/android/src/main/java/com/apollohg/editor/EditorBoxDrawing.kt +234 -0
  21. package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +87 -0
  22. package/android/src/main/java/com/apollohg/editor/EditorCodeHighlighting.kt +120 -0
  23. package/android/src/main/java/com/apollohg/editor/EditorDecorationLayer.java +18 -0
  24. package/android/src/main/java/com/apollohg/editor/EditorDocumentLayout.kt +626 -0
  25. package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +1042 -0
  26. package/android/src/main/java/com/apollohg/editor/EditorEditTextAnnotations.kt +191 -0
  27. package/android/src/main/java/com/apollohg/editor/EditorEditTextCaret.kt +100 -0
  28. package/android/src/main/java/com/apollohg/editor/EditorEditTextClipboard.kt +101 -0
  29. package/android/src/main/java/com/apollohg/editor/EditorEditTextComposition.kt +220 -0
  30. package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +316 -0
  31. package/android/src/main/java/com/apollohg/editor/EditorEditTextCorrection.kt +303 -0
  32. package/android/src/main/java/com/apollohg/editor/EditorEditTextDeferredUpdates.kt +327 -0
  33. package/android/src/main/java/com/apollohg/editor/EditorEditTextDeletion.kt +258 -0
  34. package/android/src/main/java/com/apollohg/editor/EditorEditTextDiagnostics.kt +83 -0
  35. package/android/src/main/java/com/apollohg/editor/EditorEditTextDragDrop.kt +67 -0
  36. package/android/src/main/java/com/apollohg/editor/EditorEditTextExternalComposition.kt +357 -0
  37. package/android/src/main/java/com/apollohg/editor/EditorEditTextHardwareInput.kt +277 -0
  38. package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +280 -0
  39. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputLifecycle.kt +183 -0
  40. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputPreparation.kt +79 -0
  41. package/android/src/main/java/com/apollohg/editor/EditorEditTextInputTraits.kt +384 -0
  42. package/android/src/main/java/com/apollohg/editor/EditorEditTextLayout.kt +100 -0
  43. package/android/src/main/java/com/apollohg/editor/EditorEditTextNativeMutation.kt +243 -0
  44. package/android/src/main/java/com/apollohg/editor/EditorEditTextPlaceholder.kt +154 -0
  45. package/android/src/main/java/com/apollohg/editor/EditorEditTextRenderPatch.kt +347 -0
  46. package/android/src/main/java/com/apollohg/editor/EditorEditTextRendering.kt +313 -0
  47. package/android/src/main/java/com/apollohg/editor/EditorEditTextRustCommands.kt +281 -0
  48. package/android/src/main/java/com/apollohg/editor/EditorEditTextSelection.kt +352 -0
  49. package/android/src/main/java/com/apollohg/editor/EditorEditTextState.kt +113 -0
  50. package/android/src/main/java/com/apollohg/editor/EditorEditTextTaskMarkers.kt +92 -0
  51. package/android/src/main/java/com/apollohg/editor/EditorEditTextTextCommit.kt +327 -0
  52. package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +84 -0
  53. package/android/src/main/java/com/apollohg/editor/EditorEditTextUpdates.kt +245 -0
  54. package/android/src/main/java/com/apollohg/editor/EditorGlyphHeightCursorDrawable.kt +42 -0
  55. package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +13 -0
  56. package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +923 -0
  57. package/android/src/main/java/com/apollohg/editor/EditorInputCoordinates.kt +191 -0
  58. package/android/src/main/java/com/apollohg/editor/EditorInputCorrection.kt +107 -0
  59. package/android/src/main/java/com/apollohg/editor/EditorInputCursorGeometry.kt +128 -0
  60. package/android/src/main/java/com/apollohg/editor/EditorInputSnapshots.kt +134 -0
  61. package/android/src/main/java/com/apollohg/editor/EditorInputSurroundingDeletion.kt +321 -0
  62. package/android/src/main/java/com/apollohg/editor/EditorKeyboardToolbarView.kt +851 -0
  63. package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +192 -0
  64. package/android/src/main/java/com/apollohg/editor/EditorReconciliationWatcher.kt +46 -0
  65. package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +303 -0
  66. package/android/src/main/java/com/apollohg/editor/EditorStyleSheetSpans.kt +253 -0
  67. package/android/src/main/java/com/apollohg/editor/EditorSurfaceInputConnection.kt +67 -0
  68. package/android/src/main/java/com/apollohg/editor/EditorTextDecorationDrawing.kt +143 -0
  69. package/android/src/main/java/com/apollohg/editor/EditorTextSurface.kt +712 -0
  70. package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceDragDrop.kt +94 -0
  71. package/android/src/main/java/com/apollohg/editor/EditorTextSurfaceInteraction.kt +708 -0
  72. package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +650 -0
  73. package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +912 -0
  74. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterMutations.kt +402 -0
  75. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterRendering.kt +123 -0
  76. package/android/src/main/java/com/apollohg/editor/EditorV2AdapterSelection.kt +203 -0
  77. package/android/src/main/java/com/apollohg/editor/EditorV2Backend.kt +120 -0
  78. package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +87 -0
  79. package/android/src/main/java/com/apollohg/editor/EditorV2Registry.kt +97 -0
  80. package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +409 -0
  81. package/android/src/main/java/com/apollohg/editor/ExpoAutoGrowStyleSizePublisher.kt +34 -0
  82. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +266 -0
  83. package/android/src/main/java/com/apollohg/editor/ImeTextCoordinateMapper.kt +102 -0
  84. package/android/src/main/java/com/apollohg/editor/KeyboardToolbarImeAnimationController.kt +101 -0
  85. package/android/src/main/java/com/apollohg/editor/MentionSuggestionChipView.kt +234 -0
  86. package/android/src/main/java/com/apollohg/editor/NativeCollaborationTransportRegistry.kt +493 -0
  87. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +813 -0
  88. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewAtoms.kt +68 -0
  89. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewBinding.kt +126 -0
  90. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewDiagnostics.kt +208 -0
  91. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewErrors.kt +146 -0
  92. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewEvents.kt +150 -0
  93. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewExternalComposition.kt +39 -0
  94. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewFocus.kt +176 -0
  95. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewLifecycle.kt +302 -0
  96. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewMentions.kt +259 -0
  97. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewNativeActions.kt +179 -0
  98. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewOutsideTap.kt +214 -0
  99. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPendingUpdates.kt +376 -0
  100. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewProperties.kt +234 -0
  101. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewPropertyRetries.kt +197 -0
  102. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewToolbar.kt +285 -0
  103. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewTypes.kt +28 -0
  104. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoViewUpdates.kt +271 -0
  105. package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +687 -0
  106. package/android/src/main/java/com/apollohg/editor/NativeEditorOutsideTapDispatcher.kt +380 -0
  107. package/android/src/main/java/com/apollohg/editor/NativeEditorV2Numbers.kt +45 -0
  108. package/android/src/main/java/com/apollohg/editor/NativeEditorViewRegistry.kt +372 -0
  109. package/android/src/main/java/com/apollohg/editor/NativeImagePipeline.kt +22 -0
  110. package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +193 -0
  111. package/android/src/main/java/com/apollohg/editor/NativeToolbarIcons.kt +177 -0
  112. package/android/src/main/java/com/apollohg/editor/NativeToolbarItem.kt +274 -0
  113. package/android/src/main/java/com/apollohg/editor/OkHttpCollaborationSocket.kt +102 -0
  114. package/android/src/main/java/com/apollohg/editor/OrderedListMarkerFormatter.kt +130 -0
  115. package/android/src/main/java/com/apollohg/editor/PointerTransparentView.java +18 -0
  116. package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +128 -0
  117. package/android/src/main/java/com/apollohg/editor/ProseViewerView.kt +854 -0
  118. package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +314 -0
  119. package/android/src/main/java/com/apollohg/editor/RenderBlockSpans.kt +314 -0
  120. package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +224 -0
  121. package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +407 -0
  122. package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +544 -0
  123. package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +631 -0
  124. package/android/src/main/java/com/apollohg/editor/RenderBridgeParagraphs.kt +215 -0
  125. package/android/src/main/java/com/apollohg/editor/RenderImageDecoder.kt +748 -0
  126. package/android/src/main/java/com/apollohg/editor/RenderImageLoader.kt +900 -0
  127. package/android/src/main/java/com/apollohg/editor/RenderLayoutConstants.kt +117 -0
  128. package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +337 -0
  129. package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +182 -0
  130. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +951 -0
  131. package/android/src/main/java/com/apollohg/editor/SharedNativeImagePipeline.kt +78 -0
  132. package/android/src/main/java/com/apollohg/editor/UniffiEditorV2Backend.kt +261 -0
  133. package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1656 -0
  134. package/android/src/main/java/com/apollohg/editor/viewer/FabricAttachmentSidecars.kt +80 -0
  135. package/android/src/main/java/com/apollohg/editor/viewer/FabricLeaseHandleBridge.kt +59 -0
  136. package/android/src/main/java/com/apollohg/editor/viewer/FallbackSelectionGeometry.kt +450 -0
  137. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseDrawingView.kt +678 -0
  138. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseInstrumentation.kt +608 -0
  139. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayout.kt +186 -0
  140. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutCache.kt +393 -0
  141. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseLayoutRegistry.kt +828 -0
  142. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseTheme.kt +316 -0
  143. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerManager.kt +900 -0
  144. package/android/src/main/java/com/apollohg/editor/viewer/PreparedProseViewerPackage.kt +16 -0
  145. package/android/src/main/java/com/apollohg/editor/viewer/PreparedViewerAtoms.kt +89 -0
  146. package/android/src/main/java/com/apollohg/editor/viewer/ViewerCodeHighlighting.kt +91 -0
  147. package/android/src/main/java/com/apollohg/editor/viewer/ViewerDocument.kt +453 -0
  148. package/android/src/main/java/com/apollohg/editor/viewer/ViewerFontEnvironment.kt +282 -0
  149. package/android/src/main/java/com/apollohg/editor/viewer/ViewerImagePipeline.kt +460 -0
  150. package/android/src/main/jni/CMakeLists.txt +85 -0
  151. package/android/src/main/jni/PreparedProseViewerMeasurementsManager.cpp +334 -0
  152. package/android/src/main/jni/ReactNativeProseEditorSpec.h +17 -0
  153. package/android/src/main/jni/reactnativeproseeditor.h +6 -0
  154. package/android/src/main/res/values/attrs.xml +6 -0
  155. package/android/src/release/java/com/apollohg/editor/viewer/PreparedProseDrawInstrumentation.kt +9 -0
  156. package/app.plugin.js +62 -0
  157. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseMeasurementsManager.h +62 -0
  158. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerComponentDescriptor.h +30 -0
  159. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.cpp +192 -0
  160. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerShadowNode.h +46 -0
  161. package/common/cpp/react/renderer/components/PreparedProseViewer/PreparedProseViewerState.h +145 -0
  162. package/dist/AtomHost.d.ts +17 -0
  163. package/dist/AtomHost.js +135 -0
  164. package/dist/DefaultAtomChip.d.ts +2 -0
  165. package/dist/DefaultAtomChip.js +22 -0
  166. package/dist/EditorAddon.d.ts +32 -0
  167. package/dist/EditorAddon.js +29 -0
  168. package/dist/EditorAddonNormalization.d.ts +6 -0
  169. package/dist/EditorAddonNormalization.js +48 -0
  170. package/dist/EditorMentionThemeNormalization.d.ts +8 -0
  171. package/dist/EditorMentionThemeNormalization.js +22 -0
  172. package/dist/EditorMentionThemeValidation.d.ts +12 -0
  173. package/dist/EditorMentionThemeValidation.js +95 -0
  174. package/dist/EditorStyleSheet.d.ts +4 -0
  175. package/dist/EditorStyleSheet.js +10 -0
  176. package/dist/EditorStyleSheetNormalization.d.ts +3 -0
  177. package/dist/EditorStyleSheetNormalization.js +226 -0
  178. package/dist/EditorStyleSheetTypes.d.ts +151 -0
  179. package/dist/EditorStyleSheetTypes.js +2 -0
  180. package/dist/EditorTheme.d.ts +117 -0
  181. package/dist/EditorTheme.js +41 -0
  182. package/dist/EditorToolbar.d.ts +4 -0
  183. package/dist/EditorToolbar.js +17 -0
  184. package/dist/EditorToolbarComponent.d.ts +13 -0
  185. package/dist/EditorToolbarComponent.js +24 -0
  186. package/dist/EditorToolbarItems.d.ts +9 -0
  187. package/dist/EditorToolbarItems.js +58 -0
  188. package/dist/EditorToolbarRegistry.d.ts +59 -0
  189. package/dist/EditorToolbarRegistry.js +175 -0
  190. package/dist/EditorToolbarTypes.d.ts +273 -0
  191. package/dist/EditorToolbarTypes.js +2 -0
  192. package/dist/EditorToolbarVisuals.d.ts +154 -0
  193. package/dist/EditorToolbarVisuals.js +228 -0
  194. package/dist/EditorUpdateRevision.d.ts +8 -0
  195. package/dist/EditorUpdateRevision.js +22 -0
  196. package/dist/ExternalTextComposition.d.ts +45 -0
  197. package/dist/ExternalTextComposition.js +537 -0
  198. package/dist/ImageLoadingPolicy.d.ts +54 -0
  199. package/dist/ImageLoadingPolicy.js +60 -0
  200. package/dist/NativeEditorBoundaryError.d.ts +111 -0
  201. package/dist/NativeEditorBoundaryError.js +268 -0
  202. package/dist/NativeEditorBridge.d.ts +9 -0
  203. package/dist/NativeEditorBridge.js +37 -0
  204. package/dist/NativeEditorCollaborationTransport.d.ts +154 -0
  205. package/dist/NativeEditorCollaborationTransport.js +275 -0
  206. package/dist/NativeEditorCreateConfig.d.ts +16 -0
  207. package/dist/NativeEditorCreateConfig.js +249 -0
  208. package/dist/NativeEditorCreateJson.d.ts +76 -0
  209. package/dist/NativeEditorCreateJson.js +474 -0
  210. package/dist/NativeEditorCreateValidation.d.ts +32 -0
  211. package/dist/NativeEditorCreateValidation.js +150 -0
  212. package/dist/NativeEditorDocumentBridge.d.ts +72 -0
  213. package/dist/NativeEditorDocumentBridge.js +305 -0
  214. package/dist/NativeEditorDocumentHandle.d.ts +70 -0
  215. package/dist/NativeEditorDocumentHandle.js +89 -0
  216. package/dist/NativeEditorLocalAwareness.d.ts +34 -0
  217. package/dist/NativeEditorLocalAwareness.js +153 -0
  218. package/dist/NativeEditorNativeModule.d.ts +48 -0
  219. package/dist/NativeEditorNativeModule.js +34 -0
  220. package/dist/NativeEditorRenderNormalization.d.ts +22 -0
  221. package/dist/NativeEditorRenderNormalization.js +274 -0
  222. package/dist/NativeEditorResultNormalization.d.ts +108 -0
  223. package/dist/NativeEditorResultNormalization.js +400 -0
  224. package/dist/NativeEditorTypes.d.ts +318 -0
  225. package/dist/NativeEditorTypes.js +15 -0
  226. package/dist/NativeEditorV2Decimal.d.ts +5 -0
  227. package/dist/NativeEditorV2Decimal.js +18 -0
  228. package/dist/NativeProseViewer.d.ts +122 -0
  229. package/dist/NativeProseViewer.js +130 -0
  230. package/dist/NativeRichTextEditor.d.ts +3 -0
  231. package/dist/NativeRichTextEditor.js +6 -0
  232. package/dist/ResourceLimits.d.ts +100 -0
  233. package/dist/ResourceLimits.js +80 -0
  234. package/dist/RichTextEditor.d.ts +4 -0
  235. package/dist/RichTextEditor.js +25 -0
  236. package/dist/RichTextEditorNativeTypes.d.ts +122 -0
  237. package/dist/RichTextEditorNativeTypes.js +2 -0
  238. package/dist/RichTextEditorNativeView.d.ts +12 -0
  239. package/dist/RichTextEditorNativeView.js +15 -0
  240. package/dist/RichTextEditorSerialization.d.ts +53 -0
  241. package/dist/RichTextEditorSerialization.js +283 -0
  242. package/dist/RichTextEditorTypes.d.ts +281 -0
  243. package/dist/RichTextEditorTypes.js +2 -0
  244. package/dist/ViewerConfiguration.d.ts +22 -0
  245. package/dist/ViewerConfiguration.js +6 -0
  246. package/dist/YjsCollaboration.d.ts +146 -0
  247. package/dist/YjsCollaboration.js +496 -0
  248. package/dist/addons.d.ts +193 -0
  249. package/dist/addons.js +122 -0
  250. package/dist/atomConstants.d.ts +3 -0
  251. package/dist/atomConstants.js +6 -0
  252. package/dist/atomInstances.d.ts +35 -0
  253. package/dist/atomInstances.js +79 -0
  254. package/dist/atomPolicy.d.ts +4 -0
  255. package/dist/atomPolicy.js +47 -0
  256. package/dist/atomUpdates.d.ts +2 -0
  257. package/dist/atomUpdates.js +38 -0
  258. package/dist/atoms.d.ts +92 -0
  259. package/dist/atoms.js +222 -0
  260. package/dist/attributeValidation.d.ts +4 -0
  261. package/dist/attributeValidation.js +97 -0
  262. package/dist/contentExpression.d.ts +6 -0
  263. package/dist/contentExpression.js +335 -0
  264. package/dist/index.d.ts +21 -0
  265. package/dist/index.js +78 -0
  266. package/dist/listMarkers.d.ts +15 -0
  267. package/dist/listMarkers.js +21 -0
  268. package/dist/schemaDefinition.d.ts +143 -0
  269. package/dist/schemaDefinition.js +206 -0
  270. package/dist/schemaDocument.d.ts +17 -0
  271. package/dist/schemaDocument.js +69 -0
  272. package/dist/schemaDocumentConstruction.d.ts +5 -0
  273. package/dist/schemaDocumentConstruction.js +125 -0
  274. package/dist/schemaNormalization.d.ts +29 -0
  275. package/dist/schemaNormalization.js +313 -0
  276. package/dist/schemaPresets.d.ts +68 -0
  277. package/dist/schemaPresets.js +198 -0
  278. package/dist/schemaResolution.d.ts +9 -0
  279. package/dist/schemaResolution.js +211 -0
  280. package/dist/schemas.d.ts +5 -0
  281. package/dist/schemas.js +28 -0
  282. package/dist/specs/PreparedProseViewerNativeComponent.d.ts +37 -0
  283. package/dist/specs/PreparedProseViewerNativeComponent.js +6 -0
  284. package/dist/useEditorToolbarInteractions.d.ts +12 -0
  285. package/dist/useEditorToolbarInteractions.js +218 -0
  286. package/dist/useEditorToolbarItems.d.ts +8 -0
  287. package/dist/useEditorToolbarItems.js +280 -0
  288. package/dist/useEditorToolbarPresentation.d.ts +4 -0
  289. package/dist/useEditorToolbarPresentation.js +203 -0
  290. package/dist/useEditorToolbarState.d.ts +63 -0
  291. package/dist/useEditorToolbarState.js +108 -0
  292. package/dist/useFocusPreservingFrames.d.ts +17 -0
  293. package/dist/useFocusPreservingFrames.js +108 -0
  294. package/dist/useNativeEditor.d.ts +66 -0
  295. package/dist/useNativeEditor.js +370 -0
  296. package/dist/useRichTextEditorCommands.d.ts +21 -0
  297. package/dist/useRichTextEditorCommands.js +299 -0
  298. package/dist/useRichTextEditorEvents.d.ts +18 -0
  299. package/dist/useRichTextEditorEvents.js +216 -0
  300. package/dist/useRichTextEditorMentions.d.ts +11 -0
  301. package/dist/useRichTextEditorMentions.js +316 -0
  302. package/dist/useRichTextEditorPresentation.d.ts +5 -0
  303. package/dist/useRichTextEditorPresentation.js +178 -0
  304. package/dist/useRichTextEditorState.d.ts +180 -0
  305. package/dist/useRichTextEditorState.js +405 -0
  306. package/dist/useRichTextEditorUpdates.d.ts +10 -0
  307. package/dist/useRichTextEditorUpdates.js +308 -0
  308. package/dist/useViewerAtoms.d.ts +38 -0
  309. package/dist/useViewerAtoms.js +288 -0
  310. package/expo-module.config.json +13 -0
  311. package/ios/EditorAddons.swift +354 -0
  312. package/ios/EditorCodeHighlighting.swift +184 -0
  313. package/ios/EditorCore.xcframework/Info.plist +44 -0
  314. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  315. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  316. package/ios/EditorInteractionOverlays.swift +697 -0
  317. package/ios/EditorLayoutManager.swift +971 -0
  318. package/ios/EditorNodeTypes.swift +37 -0
  319. package/ios/EditorStyleSheet.swift +461 -0
  320. package/ios/EditorTextView+AttributedPatch.swift +305 -0
  321. package/ios/EditorTextView+Binding.swift +61 -0
  322. package/ios/EditorTextView+Caret.swift +333 -0
  323. package/ios/EditorTextView+Commands.swift +203 -0
  324. package/ios/EditorTextView+Delegate.swift +65 -0
  325. package/ios/EditorTextView+Diagnostics.swift +225 -0
  326. package/ios/EditorTextView+DragDrop.swift +290 -0
  327. package/ios/EditorTextView+ExternalComposition.swift +364 -0
  328. package/ios/EditorTextView+Images.swift +354 -0
  329. package/ios/EditorTextView+InputTraits.swift +200 -0
  330. package/ios/EditorTextView+KeyboardInput.swift +318 -0
  331. package/ios/EditorTextView+Layout.swift +245 -0
  332. package/ios/EditorTextView+MarkedText.swift +267 -0
  333. package/ios/EditorTextView+NativeInputLifecycle.swift +372 -0
  334. package/ios/EditorTextView+NativeMutation.swift +471 -0
  335. package/ios/EditorTextView+Reconciliation.swift +97 -0
  336. package/ios/EditorTextView+RenderPatch.swift +573 -0
  337. package/ios/EditorTextView+Rendering.swift +539 -0
  338. package/ios/EditorTextView+Selection.swift +542 -0
  339. package/ios/EditorTextView.swift +953 -0
  340. package/ios/EditorTheme.swift +626 -0
  341. package/ios/EditorV2Adapter+Commands.swift +204 -0
  342. package/ios/EditorV2Adapter+Envelope.swift +238 -0
  343. package/ios/EditorV2Adapter+Mutation.swift +392 -0
  344. package/ios/EditorV2Adapter+Registry.swift +185 -0
  345. package/ios/EditorV2Adapter+RenderState.swift +360 -0
  346. package/ios/EditorV2Adapter+RenderValidation.swift +420 -0
  347. package/ios/EditorV2Adapter+Selection.swift +329 -0
  348. package/ios/EditorV2Adapter+TextCommands.swift +430 -0
  349. package/ios/EditorV2Adapter.swift +376 -0
  350. package/ios/EditorV2Shadow.swift +353 -0
  351. package/ios/Generated_editor_core.swift +2378 -0
  352. package/ios/NativeCodeHighlighting.swift +159 -0
  353. package/ios/NativeCollaborationSocket.swift +144 -0
  354. package/ios/NativeCollaborationTransport+Protocol.swift +258 -0
  355. package/ios/NativeCollaborationTransport+Socket.swift +379 -0
  356. package/ios/NativeCollaborationTransport+Validation.swift +103 -0
  357. package/ios/NativeCollaborationTransport.swift +322 -0
  358. package/ios/NativeCollaborationTransportRegistry.swift +521 -0
  359. package/ios/NativeEditorExpoView+AccessoryRetry.swift +219 -0
  360. package/ios/NativeEditorExpoView+Binding.swift +268 -0
  361. package/ios/NativeEditorExpoView+Diagnostics.swift +66 -0
  362. package/ios/NativeEditorExpoView+Events.swift +131 -0
  363. package/ios/NativeEditorExpoView+Focus.swift +169 -0
  364. package/ios/NativeEditorExpoView+MentionRetry.swift +288 -0
  365. package/ios/NativeEditorExpoView+Mentions.swift +288 -0
  366. package/ios/NativeEditorExpoView+Properties.swift +300 -0
  367. package/ios/NativeEditorExpoView+Toolbar.swift +86 -0
  368. package/ios/NativeEditorExpoView+Updates.swift +312 -0
  369. package/ios/NativeEditorExpoView.swift +349 -0
  370. package/ios/NativeEditorModule.swift +856 -0
  371. package/ios/NativeEditorViewRegistry.swift +226 -0
  372. package/ios/NativeToolbar+ButtonStyle.swift +214 -0
  373. package/ios/NativeToolbar+Buttons.swift +228 -0
  374. package/ios/NativeToolbar+Diagnostics.swift +114 -0
  375. package/ios/NativeToolbar+Menu.swift +101 -0
  376. package/ios/NativeToolbar+Models.swift +519 -0
  377. package/ios/NativeToolbar+Placeholder.swift +39 -0
  378. package/ios/NativeToolbar.swift +612 -0
  379. package/ios/OrderedListMarkerFormatter.swift +103 -0
  380. package/ios/PositionBridge.swift +596 -0
  381. package/ios/ProseViewerView.swift +462 -0
  382. package/ios/RenderBridge+Atoms.swift +288 -0
  383. package/ios/RenderBridge+Attachments.swift +246 -0
  384. package/ios/RenderBridge+BlockBoundaries.swift +313 -0
  385. package/ios/RenderBridge+Style.swift +343 -0
  386. package/ios/RenderBridge.swift +704 -0
  387. package/ios/RichTextEditorView.swift +930 -0
  388. package/ios/SharedNativeImagePipeline+Cache.swift +189 -0
  389. package/ios/SharedNativeImagePipeline+Transport.swift +328 -0
  390. package/ios/SharedNativeImagePipeline.swift +533 -0
  391. package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +436 -0
  392. package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +322 -0
  393. package/ios/Viewer/CoreTextProseLayoutEngine.swift +635 -0
  394. package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.h +4 -0
  395. package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +767 -0
  396. package/ios/Viewer/Fabric/PreparedProseMeasurementsManager.mm +118 -0
  397. package/ios/Viewer/NativeSVGImageDecoder.h +9 -0
  398. package/ios/Viewer/NativeSVGImageDecoder.mm +106 -0
  399. package/ios/Viewer/NativeSVGPreflight.h +11 -0
  400. package/ios/Viewer/NativeSVGPreflight.mm +219 -0
  401. package/ios/Viewer/PreparedProseDrawingView.swift +639 -0
  402. package/ios/Viewer/PreparedProseInstrumentation.swift +387 -0
  403. package/ios/Viewer/PreparedProseLayout.swift +326 -0
  404. package/ios/Viewer/PreparedProseLayoutCache.swift +766 -0
  405. package/ios/Viewer/PreparedProseLayoutRegistry+Artifacts.swift +377 -0
  406. package/ios/Viewer/PreparedProseLayoutRegistry+Fabric.swift +582 -0
  407. package/ios/Viewer/PreparedProseLayoutRegistry+FabricOwnership.swift +128 -0
  408. package/ios/Viewer/PreparedProseLayoutRegistry.swift +381 -0
  409. package/ios/Viewer/ViewerDocument.swift +575 -0
  410. package/ios/Viewer/ViewerFontEnvironment.swift +283 -0
  411. package/ios/Viewer/ViewerImagePipeline.swift +478 -0
  412. package/ios/editor_coreFFI/editor_coreFFI.h +1023 -0
  413. package/ios/editor_coreFFI/module.modulemap +7 -0
  414. package/package.json +151 -0
  415. package/react-native.config.js +17 -0
  416. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  417. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  418. package/rust/android/x86/libeditor_core.so +0 -0
  419. package/rust/android/x86_64/libeditor_core.so +0 -0
  420. package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +3146 -0
  421. package/src/AtomHost.tsx +180 -0
  422. package/src/DefaultAtomChip.tsx +27 -0
  423. package/src/EditorAddon.ts +65 -0
  424. package/src/EditorAddonNormalization.ts +49 -0
  425. package/src/EditorMentionThemeNormalization.ts +25 -0
  426. package/src/EditorMentionThemeValidation.ts +133 -0
  427. package/src/EditorStyleSheet.ts +9 -0
  428. package/src/EditorStyleSheetNormalization.ts +219 -0
  429. package/src/EditorStyleSheetTypes.ts +189 -0
  430. package/src/EditorTheme.ts +196 -0
  431. package/src/EditorToolbar.tsx +31 -0
  432. package/src/EditorToolbarComponent.tsx +23 -0
  433. package/src/EditorToolbarItems.tsx +64 -0
  434. package/src/EditorToolbarRegistry.tsx +254 -0
  435. package/src/EditorToolbarTypes.tsx +322 -0
  436. package/src/EditorToolbarVisuals.tsx +270 -0
  437. package/src/EditorUpdateRevision.ts +26 -0
  438. package/src/ExternalTextComposition.ts +658 -0
  439. package/src/ImageLoadingPolicy.ts +125 -0
  440. package/src/NativeEditorBoundaryError.ts +345 -0
  441. package/src/NativeEditorBridge.ts +93 -0
  442. package/src/NativeEditorCollaborationTransport.ts +493 -0
  443. package/src/NativeEditorCreateConfig.ts +326 -0
  444. package/src/NativeEditorCreateJson.ts +579 -0
  445. package/src/NativeEditorCreateValidation.ts +178 -0
  446. package/src/NativeEditorDocumentBridge.ts +485 -0
  447. package/src/NativeEditorDocumentHandle.ts +168 -0
  448. package/src/NativeEditorLocalAwareness.ts +168 -0
  449. package/src/NativeEditorNativeModule.ts +97 -0
  450. package/src/NativeEditorRenderNormalization.ts +320 -0
  451. package/src/NativeEditorResultNormalization.ts +469 -0
  452. package/src/NativeEditorTypes.ts +366 -0
  453. package/src/NativeEditorV2Decimal.ts +13 -0
  454. package/src/NativeProseViewer.tsx +356 -0
  455. package/src/NativeRichTextEditor.tsx +33 -0
  456. package/src/ResourceLimits.ts +187 -0
  457. package/src/RichTextEditor.tsx +27 -0
  458. package/src/RichTextEditorNativeTypes.tsx +144 -0
  459. package/src/RichTextEditorNativeView.tsx +21 -0
  460. package/src/RichTextEditorSerialization.tsx +381 -0
  461. package/src/RichTextEditorTypes.tsx +327 -0
  462. package/src/ViewerConfiguration.ts +23 -0
  463. package/src/YjsCollaboration.ts +692 -0
  464. package/src/addons.ts +311 -0
  465. package/src/atomConstants.ts +3 -0
  466. package/src/atomInstances.ts +123 -0
  467. package/src/atomPolicy.ts +47 -0
  468. package/src/atomUpdates.ts +52 -0
  469. package/src/atoms.ts +379 -0
  470. package/src/attributeValidation.ts +116 -0
  471. package/src/contentExpression.ts +338 -0
  472. package/src/index.ts +295 -0
  473. package/src/listMarkers.ts +18 -0
  474. package/src/schemaDefinition.ts +395 -0
  475. package/src/schemaDocument.ts +92 -0
  476. package/src/schemaDocumentConstruction.ts +148 -0
  477. package/src/schemaNormalization.ts +367 -0
  478. package/src/schemaPresets.ts +250 -0
  479. package/src/schemaResolution.ts +242 -0
  480. package/src/schemas.ts +43 -0
  481. package/src/specs/PreparedProseViewerNativeComponent.ts +40 -0
  482. package/src/useEditorToolbarInteractions.tsx +329 -0
  483. package/src/useEditorToolbarItems.tsx +390 -0
  484. package/src/useEditorToolbarPresentation.tsx +483 -0
  485. package/src/useEditorToolbarState.tsx +172 -0
  486. package/src/useFocusPreservingFrames.ts +171 -0
  487. package/src/useNativeEditor.ts +534 -0
  488. package/src/useRichTextEditorCommands.tsx +478 -0
  489. package/src/useRichTextEditorEvents.tsx +342 -0
  490. package/src/useRichTextEditorMentions.tsx +441 -0
  491. package/src/useRichTextEditorPresentation.tsx +563 -0
  492. package/src/useRichTextEditorState.tsx +620 -0
  493. package/src/useRichTextEditorUpdates.tsx +403 -0
  494. package/src/useViewerAtoms.tsx +427 -0
@@ -0,0 +1,951 @@
1
+ package com.apollohg.editor
2
+
3
+ import android.content.Context
4
+ import android.graphics.Color
5
+ import android.graphics.Rect
6
+ import android.graphics.RectF
7
+ import android.graphics.drawable.GradientDrawable
8
+ import android.os.Handler
9
+ import android.os.Looper
10
+ import android.util.AttributeSet
11
+ import android.view.MotionEvent
12
+ import android.view.View
13
+ import android.view.ViewConfiguration
14
+ import android.view.ViewGroup
15
+ import android.widget.FrameLayout
16
+ import android.widget.LinearLayout
17
+ import android.widget.ScrollView
18
+ import kotlin.math.roundToInt
19
+
20
+ internal data class AtomLayoutPosition(
21
+ val key: String,
22
+ val xPx: Int,
23
+ val yPx: Int,
24
+ val heightPx: Int,
25
+ val widthPx: Int
26
+ )
27
+
28
+ /** Container view that owns the native editor text field. */
29
+ class RichTextEditorView @JvmOverloads constructor(
30
+ context: Context,
31
+ attrs: AttributeSet? = null,
32
+ defStyleAttr: Int = 0
33
+ ) : LinearLayout(context, attrs, defStyleAttr) {
34
+ val editorViewport: FrameLayout
35
+ val editorContentFrame: FrameLayout
36
+
37
+ private inner class EditorScrollView(context: Context) : ScrollView(context) {
38
+ override fun computeScrollDeltaToGetChildRectOnScreen(rect: Rect): Int {
39
+ val delta = super.computeScrollDeltaToGetChildRectOnScreen(rect)
40
+ val child = getChildAt(0) ?: return delta
41
+ val screenBottom = scrollY + height
42
+ if (viewportBottomInsetPx <= 0 || rect.bottom <= child.bottom ||
43
+ rect.bottom <= screenBottom || rect.top <= scrollY
44
+ ) {
45
+ return delta
46
+ }
47
+
48
+ // ScrollView's reveal clamp omits the padding reserved for the keyboard.
49
+ val requested = if (rect.height() >
50
+ height
51
+ ) {
52
+ rect.top - scrollY
53
+ } else {
54
+ rect.bottom - screenBottom
55
+ }
56
+ val available = (child.bottom + paddingBottom - screenBottom).coerceAtLeast(0)
57
+ return maxOf(delta, minOf(requested, available))
58
+ }
59
+
60
+ private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
61
+ private var atomDown = false
62
+ private var downX = 0f
63
+ private var downY = 0f
64
+ private var horizontalAtomGesture = false
65
+ private fun updateParentIntercept(action: Int) {
66
+ val canScroll = canScrollVertically(-1) || canScrollVertically(1)
67
+ if (!canScroll) return
68
+ when (action) {
69
+ MotionEvent.ACTION_DOWN,
70
+ MotionEvent.ACTION_MOVE -> parent?.requestDisallowInterceptTouchEvent(true)
71
+
72
+ MotionEvent.ACTION_UP,
73
+ MotionEvent.ACTION_CANCEL -> parent?.requestDisallowInterceptTouchEvent(false)
74
+ }
75
+ }
76
+
77
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
78
+ if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
79
+ downX = ev.x
80
+ downY = ev.y
81
+ horizontalAtomGesture = false
82
+ atomDown = atomHostViews.values.any { child ->
83
+ val x = ev.x + scrollX - editorContentFrame.left
84
+ val y = ev.y + scrollY - editorContentFrame.top
85
+ child.visibility == View.VISIBLE && x >= child.left && x < child.right &&
86
+ y >= child.top &&
87
+ y < child.bottom
88
+ }
89
+ pendingAtomAnchor = null
90
+ }
91
+ if (atomDown && ev.actionMasked == MotionEvent.ACTION_MOVE) {
92
+ val dx = kotlin.math.abs(ev.x - downX)
93
+ val dy = kotlin.math.abs(ev.y - downY)
94
+ if (dx > touchSlop && dx > dy) horizontalAtomGesture = true
95
+ }
96
+ if (horizontalAtomGesture) return false
97
+ updateParentIntercept(ev.actionMasked)
98
+ return super.onInterceptTouchEvent(ev)
99
+ }
100
+
101
+ override fun onTouchEvent(ev: MotionEvent): Boolean {
102
+ if (ev.actionMasked == MotionEvent.ACTION_DOWN) pendingAtomAnchor = null
103
+ updateParentIntercept(ev.actionMasked)
104
+ return super.onTouchEvent(ev)
105
+ }
106
+ }
107
+
108
+ private inner class EditorContentFrame(context: Context) : FrameLayout(context) {
109
+ override fun measureChildWithMargins(
110
+ child: View,
111
+ parentWidthMeasureSpec: Int,
112
+ widthUsed: Int,
113
+ parentHeightMeasureSpec: Int,
114
+ heightUsed: Int
115
+ ) {
116
+ if (child === editorEditText) {
117
+ super.measureChildWithMargins(
118
+ child,
119
+ parentWidthMeasureSpec,
120
+ widthUsed,
121
+ parentHeightMeasureSpec,
122
+ heightUsed
123
+ )
124
+ return
125
+ }
126
+ val width =
127
+ atomHostViews.entries.firstOrNull { it.value === child }?.key?.let(::atomWidthPx)
128
+ ?: child.measuredWidth.takeIf { it > 0 }
129
+ ?: child.width.coerceAtLeast(0)
130
+ val height = renderedAtomHeightPx(child)
131
+ ?: atomHostViews.entries.firstOrNull {
132
+ it.value === child
133
+ }?.key?.let(::atomSpan)?.reservedHeightPx
134
+ ?: child.measuredHeight.takeIf { it > 0 }
135
+ ?: child.height.coerceAtLeast(0)
136
+ child.measure(
137
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
138
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
139
+ )
140
+ }
141
+
142
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
143
+ super.onLayout(changed, left, top, right, bottom)
144
+ restoreAtomScrollAnchor()
145
+ layoutAtomHostViews()
146
+ }
147
+ }
148
+
149
+ val editorEditText: EditorEditText
150
+ val editorScrollView: ScrollView
151
+ private val remoteSelectionOverlayView: RemoteSelectionOverlayView
152
+ private val imageResizeOverlayView: ImageResizeOverlayView
153
+
154
+ private var heightBehavior: EditorHeightBehavior = EditorHeightBehavior.FIXED
155
+ private var imageResizingEnabled = true
156
+ private var theme: EditorTheme? = null
157
+ private var baseBackgroundColor: Int = Color.WHITE
158
+ private var viewportBottomInsetPx: Int = 0
159
+ private var lastAtomViewportHeight = -1
160
+ internal var onAtomLayoutChange: ((Float, List<AtomLayoutPosition>) -> Unit)? = null
161
+ private var atomRenderConfiguration: AtomRenderConfiguration? = null
162
+ private val atomHostViews = linkedMapOf<String, View>()
163
+ private val atomLayoutListeners = mutableMapOf<View, View.OnLayoutChangeListener>()
164
+ private val atomMeasurementHandler = Handler(Looper.getMainLooper())
165
+ private val pendingAtomMeasurements = mutableMapOf<View, Runnable>()
166
+ private val measuredAtomHeightsPx = mutableMapOf<String, Int>()
167
+ private var atomMeasurementGeneration = 0L
168
+ private var atomMeasurementWidth = -1
169
+ private var positioningAtoms = false
170
+ private data class AtomScrollAnchor(val offset: Int, val top: Int, val scrollY: Int)
171
+ private var pendingAtomAnchor: AtomScrollAnchor? = null
172
+ private var lastAtomContentWidthPx = -1
173
+ private var lastAtomLayoutPositions = emptyList<AtomLayoutPosition>()
174
+ internal var appliedCornerRadiusPx: Float = 0f
175
+ internal var appliedBackgroundColorForTesting: Int = Color.WHITE
176
+ internal var onAutoGrowHeightMayChange: (() -> Unit)? = null
177
+
178
+ private var currentEditorId: Long = 0
179
+ private var deferEditorUnbindOnDetach = false
180
+ internal var onBeforeDetachedFromWindow: (() -> Unit)? = null
181
+
182
+ /** Binds or unbinds the Rust editor instance. */
183
+ var editorId: Long
184
+ get() = currentEditorId
185
+ set(value) {
186
+ setEditorId(value, bindEditor = true)
187
+ }
188
+
189
+ init {
190
+ orientation = VERTICAL
191
+
192
+ editorEditText = EditorEditText(context)
193
+ editorContentFrame = EditorContentFrame(context).apply {
194
+ setOnTouchListener { _, event ->
195
+ if (event.actionMasked == MotionEvent.ACTION_DOWN) {
196
+ editorEditText.isFocusableInTouchMode = true
197
+ editorEditText.requestFocus()
198
+ }
199
+ true
200
+ }
201
+ }
202
+ editorScrollView = EditorScrollView(context).apply {
203
+ clipToPadding = false
204
+ // Short content must still fill the viewport, or taps below the
205
+ // last line land on the scroll container and never focus.
206
+ isFillViewport = true
207
+ }
208
+ editorViewport = FrameLayout(context)
209
+ remoteSelectionOverlayView = RemoteSelectionOverlayView(context)
210
+ imageResizeOverlayView = ImageResizeOverlayView(context)
211
+ editorContentFrame.addView(editorEditText, createEditorLayoutParams())
212
+ editorScrollView.addView(
213
+ editorContentFrame,
214
+ FrameLayout.LayoutParams(
215
+ ViewGroup.LayoutParams.MATCH_PARENT,
216
+ ViewGroup.LayoutParams.WRAP_CONTENT
217
+ )
218
+ )
219
+ editorViewport.addView(
220
+ editorScrollView,
221
+ FrameLayout.LayoutParams(
222
+ ViewGroup.LayoutParams.MATCH_PARENT,
223
+ ViewGroup.LayoutParams.MATCH_PARENT
224
+ )
225
+ )
226
+ val decorationLayer = EditorDecorationLayer(context)
227
+ editorViewport.addView(
228
+ decorationLayer,
229
+ FrameLayout.LayoutParams(
230
+ ViewGroup.LayoutParams.MATCH_PARENT,
231
+ ViewGroup.LayoutParams.MATCH_PARENT
232
+ )
233
+ )
234
+ decorationLayer.addView(
235
+ remoteSelectionOverlayView,
236
+ FrameLayout.LayoutParams(
237
+ ViewGroup.LayoutParams.MATCH_PARENT,
238
+ ViewGroup.LayoutParams.MATCH_PARENT
239
+ )
240
+ )
241
+ decorationLayer.addView(
242
+ imageResizeOverlayView,
243
+ FrameLayout.LayoutParams(
244
+ ViewGroup.LayoutParams.MATCH_PARENT,
245
+ ViewGroup.LayoutParams.MATCH_PARENT
246
+ )
247
+ )
248
+ remoteSelectionOverlayView.bind(this)
249
+ imageResizeOverlayView.bind(this)
250
+ editorEditText.onBeforeRenderRefresh = imageResizeOverlayView::cancelActiveResize
251
+ editorScrollView.setOnScrollChangeListener { _, _, _, _, _ ->
252
+ editorEditText.surfaceViewportChanged()
253
+ refreshOverlays()
254
+ emitAtomLayoutIfAvailable(force = true)
255
+ }
256
+ editorEditText.onSelectionOrContentMayChange = { refreshOverlays() }
257
+ editorEditText.onContentSizeMayChange = {
258
+ if (heightBehavior == EditorHeightBehavior.AUTO_GROW) {
259
+ onAutoGrowHeightMayChange?.invoke()
260
+ }
261
+ }
262
+
263
+ addView(editorViewport, createContainerLayoutParams())
264
+ updateScrollContainerAppearance()
265
+ updateScrollContainerInsets()
266
+ }
267
+
268
+ fun configure(
269
+ textSizePx: Float = 16f * resources.displayMetrics.density,
270
+ textColor: Int = Color.BLACK,
271
+ backgroundColor: Int = Color.WHITE
272
+ ) {
273
+ baseBackgroundColor = backgroundColor
274
+ editorEditText.setBaseStyle(textSizePx, textColor, backgroundColor)
275
+ updateScrollContainerAppearance()
276
+ refreshOverlays()
277
+ }
278
+
279
+ fun applyTheme(theme: EditorTheme?) {
280
+ this.theme = theme
281
+ val previousScrollY = editorScrollView.scrollY
282
+ editorEditText.applyTheme(theme)
283
+ updateScrollContainerAppearance()
284
+ updateScrollContainerInsets()
285
+ if (heightBehavior == EditorHeightBehavior.FIXED) {
286
+ editorScrollView.post {
287
+ val childHeight = editorScrollView.getChildAt(0)?.height ?: 0
288
+ val maxScrollY = maxOf(
289
+ 0,
290
+ childHeight + editorScrollView.paddingTop + editorScrollView.paddingBottom -
291
+ editorScrollView.height
292
+ )
293
+ editorScrollView.scrollTo(0, previousScrollY.coerceIn(0, maxScrollY))
294
+ refreshOverlays()
295
+ }
296
+ }
297
+ refreshOverlays()
298
+ }
299
+
300
+ fun applyAtomRenderConfiguration(configuration: AtomRenderConfiguration?): Boolean {
301
+ val previous = atomRenderConfiguration
302
+ atomRenderConfiguration = configuration
303
+ val applied = editorEditText.applyAtomRenderConfiguration(resolvedAtomRenderConfiguration())
304
+ if (!applied) atomRenderConfiguration = previous
305
+ if (applied) refreshOverlays()
306
+ return applied
307
+ }
308
+
309
+ internal fun mountAtomChild(child: View, atomKey: String) {
310
+ cancelAtomMeasurement(child)
311
+ atomHostViews.entries.firstOrNull { it.value === child }?.let { existing ->
312
+ if (existing.key != atomKey) {
313
+ atomHostViews.remove(existing.key)
314
+ restoreConfiguredAtomHeight(existing.key)
315
+ }
316
+ }
317
+ atomHostViews[atomKey]?.takeIf { it !== child }?.let(::detachAtomView)
318
+ atomHostViews[atomKey] = child
319
+ if (child.parent !== editorContentFrame) {
320
+ (child.parent as? ViewGroup)?.removeView(child)
321
+ editorContentFrame.addView(child)
322
+ }
323
+ val listener = View.OnLayoutChangeListener { view, _, _, _, _, _, _, _, _ ->
324
+ if (!positioningAtoms) {
325
+ val observedHeight = view.height
326
+ val observedWidth = view.width
327
+ layoutAtomHostViews()
328
+ scheduleAtomMeasurement(view, atomKey, observedWidth, observedHeight)
329
+ }
330
+ }
331
+ atomLayoutListeners.remove(child)?.let(child::removeOnLayoutChangeListener)
332
+ atomLayoutListeners[child] = listener
333
+ child.addOnLayoutChangeListener(listener)
334
+ val initialHeight = renderedAtomHeightPx(child) ?: child.height
335
+ if (initialHeight > 0 || (initialHeight == 0 && child.isLaidOut)) {
336
+ constrainAtomHostBounds(child, child.width, initialHeight)
337
+ setAtomHeight(atomKey, initialHeight)
338
+ }
339
+ layoutAtomHostViews()
340
+ }
341
+
342
+ internal fun orderAtomChildren(children: List<View>) {
343
+ val keys = atomHostViews.entries.associate { it.value to it.key }
344
+ children.forEach { child ->
345
+ keys[child]?.let { key ->
346
+ atomHostViews.remove(key)
347
+ atomHostViews[key] = child
348
+ }
349
+ }
350
+ ensureAtomHostZOrder()
351
+ }
352
+
353
+ private fun invalidateAtomMeasurements(clearHeights: Boolean = false) {
354
+ atomMeasurementGeneration += 1
355
+ pendingAtomMeasurements.values.forEach(atomMeasurementHandler::removeCallbacks)
356
+ pendingAtomMeasurements.clear()
357
+ pendingAtomAnchor = null
358
+ if (clearHeights) measuredAtomHeightsPx.clear()
359
+ }
360
+
361
+ private fun scheduleAtomMeasurement(
362
+ child: View,
363
+ atomKey: String,
364
+ observedWidth: Int = child.width,
365
+ observedHeight: Int = child.height
366
+ ) {
367
+ if (pendingAtomMeasurements.containsKey(child)) return
368
+ val generation = atomMeasurementGeneration
369
+ val width = atomWidthPx(atomKey) ?: observedWidth.takeIf { it > 0 } ?: return
370
+ val owner = currentEditorId
371
+ // Fabric commits host bounds before descendant bounds.
372
+ val measurement = Runnable {
373
+ pendingAtomMeasurements.remove(child)
374
+ if (generation != atomMeasurementGeneration || owner != currentEditorId ||
375
+ atomHostViews[atomKey] !== child || child.parent !== editorContentFrame ||
376
+ (atomWidthPx(atomKey) ?: width) != width
377
+ ) {
378
+ return@Runnable
379
+ }
380
+ val height = if (child !is ViewGroup || child.childCount == 0) {
381
+ observedHeight.takeIf { observedWidth == width && it >= 0 }
382
+ } else {
383
+ renderedAtomHeightPx(child, width)
384
+ }
385
+ if (height == null) return@Runnable
386
+ constrainAtomHostBounds(child, width, height)
387
+ setAtomHeight(atomKey, height)
388
+ layoutAtomHostViews()
389
+ }
390
+ pendingAtomMeasurements[child] = measurement
391
+ atomMeasurementHandler.post(measurement)
392
+ }
393
+
394
+ private fun cancelAtomMeasurement(child: View) {
395
+ pendingAtomMeasurements.remove(child)?.let(atomMeasurementHandler::removeCallbacks)
396
+ }
397
+
398
+ private fun atomContentWidthPx(): Int? = (
399
+ (editorEditText.width.takeIf { it > 0 } ?: editorEditText.measuredWidth) -
400
+ editorEditText.compoundPaddingLeft - editorEditText.compoundPaddingRight
401
+ ).takeIf { it > 0 }
402
+
403
+ private fun renderedAtomHeightPx(host: View, expectedWidth: Int? = null): Int? {
404
+ val group = host as? ViewGroup
405
+ if (group == null || group.childCount == 0) {
406
+ return host.height.takeIf {
407
+ host.isLaidOut &&
408
+ (expectedWidth == null || host.width == expectedWidth)
409
+ }
410
+ }
411
+ val measurementRoot = (0 until group.childCount).asSequence().map(
412
+ group::getChildAt
413
+ ).firstOrNull {
414
+ (
415
+ it.getTag(
416
+ com.facebook.react.R.id.view_tag_native_id
417
+ ) as? String
418
+ )?.startsWith("prose-atom-content:") ==
419
+ true
420
+ }
421
+ if (measurementRoot != null) {
422
+ return measurementRoot.height.takeIf {
423
+ measurementRoot.isLaidOut &&
424
+ (expectedWidth == null || measurementRoot.width == expectedWidth)
425
+ }
426
+ }
427
+ var bottom = 0
428
+ var hasLaidOutChild = false
429
+ for (index in 0 until group.childCount) {
430
+ val child = group.getChildAt(index)
431
+ if (child.visibility != View.GONE) {
432
+ if (expectedWidth != null && child.width != expectedWidth) return null
433
+ bottom = maxOf(bottom, child.bottom)
434
+ hasLaidOutChild = hasLaidOutChild || child.isLaidOut
435
+ }
436
+ }
437
+ return bottom.takeIf { it > 0 || hasLaidOutChild }
438
+ }
439
+
440
+ private fun constrainAtomHostBounds(host: View, width: Int, height: Int) {
441
+ if (width <= 0 || height < 0) return
442
+ positioningAtoms = true
443
+ try {
444
+ host.measure(
445
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
446
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
447
+ )
448
+ host.layout(host.left, host.top, host.left + width, host.top + height)
449
+ } finally {
450
+ positioningAtoms = false
451
+ }
452
+ }
453
+
454
+ internal fun unmountAtomChild(child: View): Boolean {
455
+ val entry = atomHostViews.entries.firstOrNull { it.value === child } ?: return false
456
+ atomHostViews.remove(entry.key)
457
+ detachAtomView(child)
458
+ restoreConfiguredAtomHeight(entry.key)
459
+ layoutAtomHostViews()
460
+ return true
461
+ }
462
+
463
+ private fun restoreConfiguredAtomHeight(atomKey: String) {
464
+ measuredAtomHeightsPx.remove(atomKey)
465
+ val nodeType = atomSpan(atomKey)?.nodeType
466
+ val fallbackHeight = atomRenderConfiguration?.let { configuration ->
467
+ nodeType?.let {
468
+ configuration.reservedHeightPx(atomKey, it, resources.displayMetrics.density)
469
+ }
470
+ } ?: 0
471
+ captureAtomScrollAnchor()
472
+ editorEditText.applyAtomHeight(atomKey, fallbackHeight, resolvedAtomRenderConfiguration())
473
+ }
474
+
475
+ private fun detachAtomView(child: View) {
476
+ cancelAtomMeasurement(child)
477
+ atomLayoutListeners.remove(child)?.let(child::removeOnLayoutChangeListener)
478
+ (child.parent as? ViewGroup)?.removeView(child)
479
+ }
480
+
481
+ private fun setAtomHeight(atomKey: String, heightPx: Int) {
482
+ if (heightPx < 0 || measuredAtomHeightsPx[atomKey] == heightPx) return
483
+ measuredAtomHeightsPx[atomKey] = heightPx
484
+ captureAtomScrollAnchor()
485
+ editorEditText.applyAtomHeight(atomKey, heightPx, resolvedAtomRenderConfiguration())
486
+ }
487
+
488
+ private fun captureAtomScrollAnchor() {
489
+ if (pendingAtomAnchor != null || editorScrollView.scrollY <= 0) return
490
+ val layout = editorEditText.layout ?: return
491
+ val top = editorScrollView.scrollY - editorEditText.top - editorEditText.totalPaddingTop
492
+ val line = layout.getLineForVertical(top.coerceAtLeast(0))
493
+ pendingAtomAnchor =
494
+ AtomScrollAnchor(
495
+ layout.getLineStart(line),
496
+ layout.getLineTop(line),
497
+ editorScrollView.scrollY
498
+ )
499
+ }
500
+
501
+ private fun restoreAtomScrollAnchor() {
502
+ val anchor = pendingAtomAnchor ?: return
503
+ pendingAtomAnchor = null
504
+ val layout = editorEditText.layout ?: return
505
+ val offset = anchor.offset.coerceIn(0, layout.text.length)
506
+ val delta = layout.getLineTop(layout.getLineForOffset(offset)) - anchor.top
507
+ editorScrollView.scrollTo(
508
+ editorScrollView.scrollX,
509
+ (anchor.scrollY + delta).coerceAtLeast(0)
510
+ )
511
+ }
512
+
513
+ private fun resolvedAtomRenderConfiguration(): AtomRenderConfiguration? {
514
+ val configuration = atomRenderConfiguration ?: return null
515
+ return configuration.copy(
516
+ measuredHeightsPx =
517
+ configuration.measuredHeightsPx + measuredAtomHeightsPx
518
+ )
519
+ }
520
+
521
+ private fun atomSpan(atomKey: String): AtomBlockSpan? {
522
+ val content = editorEditText.text as? android.text.Spanned ?: return null
523
+ return content.getSpans(0, content.length, AtomBlockSpan::class.java).firstOrNull {
524
+ it.atomKey ==
525
+ atomKey
526
+ }
527
+ }
528
+
529
+ private fun atomBounds(
530
+ span: AtomBlockSpan,
531
+ content: android.text.Spanned,
532
+ layout: android.text.Layout
533
+ ): Rect? {
534
+ val offset = content.getSpanStart(span)
535
+ if (offset < 0) return null
536
+ val line = layout.getLineForOffset(offset)
537
+ val composite = layout as? EditorDocumentLayout
538
+ val left = composite?.contentLeft(line) ?: layout.getParagraphLeft(line).toFloat()
539
+ val right = composite?.contentRight(line) ?: layout.getParagraphRight(line).toFloat()
540
+ val top = composite?.textLineTop(line) ?: layout.getLineTop(line)
541
+ val x = editorEditText.left + editorEditText.compoundPaddingLeft
542
+ val y = editorEditText.top + editorEditText.totalPaddingTop
543
+ return Rect(
544
+ x + left.roundToInt(),
545
+ y + top,
546
+ x + right.roundToInt(),
547
+ y + top + span.reservedHeightPx
548
+ )
549
+ }
550
+
551
+ private fun atomWidthPx(atomKey: String): Int? {
552
+ if (atomContentWidthPx() == null) return null
553
+ val content = editorEditText.text as? android.text.Spanned ?: return atomContentWidthPx()
554
+ val layout = editorEditText.layout ?: return atomContentWidthPx()
555
+ return atomSpan(atomKey)?.let { atomBounds(it, content, layout)?.width() }
556
+ ?: atomContentWidthPx()
557
+ }
558
+
559
+ internal fun layoutAtomHostViews() {
560
+ if (positioningAtoms) return
561
+ val width = atomContentWidthPx() ?: return
562
+ val content = editorEditText.text as? android.text.Spanned ?: return
563
+ val textLayout = editorEditText.layout
564
+ if (atomMeasurementWidth != width) {
565
+ atomMeasurementWidth = width
566
+ invalidateAtomMeasurements()
567
+ }
568
+ val spanList = content.getSpans(0, content.length, AtomBlockSpan::class.java).toList()
569
+ emitAtomLayoutIfAvailable(content, textLayout, spanList)
570
+ val spans = spanList.associateBy { it.atomKey }
571
+ positioningAtoms = true
572
+ try {
573
+ for ((key, child) in atomHostViews) {
574
+ val bounds = spans[key]?.let { atomBounds(it, content, textLayout) }
575
+ if (bounds == null) {
576
+ child.visibility = View.INVISIBLE
577
+ continue
578
+ }
579
+ child.translationX = 0f
580
+ child.translationY = 0f
581
+ if (child.measuredWidth != bounds.width() ||
582
+ child.measuredHeight != bounds.height()
583
+ ) {
584
+ child.measure(
585
+ MeasureSpec.makeMeasureSpec(
586
+ bounds.width().coerceAtLeast(0),
587
+ MeasureSpec.EXACTLY
588
+ ),
589
+ MeasureSpec.makeMeasureSpec(
590
+ bounds.height().coerceAtLeast(0),
591
+ MeasureSpec.EXACTLY
592
+ )
593
+ )
594
+ }
595
+ child.layout(bounds.left, bounds.top, bounds.right, bounds.bottom)
596
+ child.visibility = View.VISIBLE
597
+ }
598
+ } finally {
599
+ positioningAtoms = false
600
+ }
601
+ ensureAtomHostZOrder()
602
+ }
603
+
604
+ private fun ensureAtomHostZOrder() {
605
+ val atoms = atomHostViews.values.filter { it.parent === editorContentFrame }
606
+ val first = editorContentFrame.childCount - atoms.size
607
+ if (first >= 0 &&
608
+ atoms.indices.all { editorContentFrame.getChildAt(first + it) === atoms[it] }
609
+ ) {
610
+ return
611
+ }
612
+ atoms.forEach(editorContentFrame::bringChildToFront)
613
+ }
614
+
615
+ internal fun emitAtomLayoutIfAvailable(force: Boolean = false) {
616
+ if (atomRenderConfiguration == null || atomContentWidthPx() == null) return
617
+ val content = editorEditText.text as? android.text.Spanned ?: return
618
+ val layout = editorEditText.layout ?: return
619
+ emitAtomLayoutIfAvailable(
620
+ content,
621
+ layout,
622
+ content.getSpans(0, content.length, AtomBlockSpan::class.java).toList(),
623
+ force
624
+ )
625
+ }
626
+
627
+ private fun emitAtomLayoutIfAvailable(
628
+ content: android.text.Spanned,
629
+ layout: android.text.Layout,
630
+ spans: List<AtomBlockSpan>,
631
+ force: Boolean = false
632
+ ) {
633
+ if (atomRenderConfiguration == null) return
634
+ val width = atomContentWidthPx() ?: return
635
+ val positions = spans.mapNotNull { span ->
636
+ atomBounds(span, content, layout)?.let {
637
+ AtomLayoutPosition(span.atomKey, it.left, it.top, it.height(), it.width())
638
+ }
639
+ }
640
+ if (force || editorScrollView.height != lastAtomViewportHeight ||
641
+ width != lastAtomContentWidthPx ||
642
+ positions != lastAtomLayoutPositions
643
+ ) {
644
+ lastAtomViewportHeight = editorScrollView.height
645
+ lastAtomContentWidthPx = width
646
+ lastAtomLayoutPositions = positions
647
+ onAtomLayoutChange?.invoke(width.toFloat(), positions)
648
+ }
649
+ }
650
+
651
+ internal fun measuredAtomHeightForTesting(atomKey: String): Int? =
652
+ measuredAtomHeightsPx[atomKey]
653
+
654
+ internal fun atomHeightRenderApplyCountForTesting(): Int =
655
+ editorEditText.atomHeightRenderApplyCountForTesting()
656
+
657
+ fun setHeightBehavior(heightBehavior: EditorHeightBehavior) {
658
+ if (this.heightBehavior == heightBehavior) return
659
+ this.heightBehavior = heightBehavior
660
+ editorEditText.setHeightBehavior(heightBehavior)
661
+ editorEditText.layoutParams = createEditorLayoutParams()
662
+ editorViewport.layoutParams = createContainerLayoutParams()
663
+ editorScrollView.isVerticalScrollBarEnabled = heightBehavior == EditorHeightBehavior.FIXED
664
+ editorScrollView.overScrollMode = if (heightBehavior == EditorHeightBehavior.FIXED) {
665
+ OVER_SCROLL_IF_CONTENT_SCROLLS
666
+ } else {
667
+ OVER_SCROLL_NEVER
668
+ }
669
+ updateScrollContainerInsets()
670
+ refreshOverlays()
671
+ requestLayout()
672
+ }
673
+
674
+ fun setImageResizingEnabled(enabled: Boolean) {
675
+ if (imageResizingEnabled == enabled) return
676
+ imageResizingEnabled = enabled
677
+ editorEditText.setImageResizingEnabled(enabled)
678
+ refreshOverlays()
679
+ }
680
+
681
+ fun setViewportBottomInsetPx(bottomInsetPx: Int) {
682
+ val clampedInset = bottomInsetPx.coerceAtLeast(0)
683
+ if (viewportBottomInsetPx == clampedInset) return
684
+ viewportBottomInsetPx = clampedInset
685
+ updateScrollContainerInsets()
686
+ editorEditText.setViewportBottomInsetPx(clampedInset)
687
+ refreshOverlays()
688
+ requestLayout()
689
+ }
690
+
691
+ fun setViewportBottomOcclusionTopOnScreenPx(topPx: Int?) {
692
+ editorEditText.setViewportBottomOcclusionTopOnScreenPx(topPx)
693
+ }
694
+
695
+ internal fun viewportBottomInsetPxForTesting(): Int = viewportBottomInsetPx
696
+
697
+ fun setRemoteSelections(selections: List<RemoteSelectionDecoration>) {
698
+ remoteSelectionOverlayView.setRemoteSelections(selections)
699
+ }
700
+
701
+ fun refreshRemoteSelections() {
702
+ if (!remoteSelectionOverlayView.hasSelectionsOrCachedGeometry()) return
703
+ remoteSelectionOverlayView.refreshGeometry()
704
+ }
705
+
706
+ fun imageResizeOverlayRectForTesting(): android.graphics.RectF? =
707
+ imageResizeOverlayView.visibleRectForTesting()
708
+
709
+ fun resizeSelectedImageForTesting(widthPx: Float, heightPx: Float) {
710
+ imageResizeOverlayView.simulateResizeForTesting(widthPx, heightPx)
711
+ }
712
+
713
+ internal fun dispatchImageResizeTouchForTesting(event: MotionEvent): Boolean =
714
+ imageResizeOverlayView.onTouchEvent(event)
715
+
716
+ fun remoteSelectionDebugSnapshotsForTesting(): List<RemoteSelectionDebugSnapshot> =
717
+ remoteSelectionOverlayView.debugSnapshotsForTesting()
718
+
719
+ fun setRemoteSelectionScalarResolverForTesting(resolver: (Long, Int) -> Int) {
720
+ remoteSelectionOverlayView.docToScalarResolver = resolver
721
+ }
722
+
723
+ fun setRemoteSelectionEditorIdForTesting(editorId: Long) {
724
+ remoteSelectionOverlayView.editorIdOverrideForTesting = editorId
725
+ }
726
+
727
+ fun setContent(html: String) {
728
+ if (editorId == 0L) return
729
+ val driver = editorEditText.v2Driver ?: return
730
+ driver.setContentHtml(html)?.let {
731
+ editorEditText.applyUpdateJSON(
732
+ it,
733
+ notifyListener = false,
734
+ refreshInputConnectionForExternalUpdate = true
735
+ )
736
+ }
737
+ }
738
+
739
+ fun setContent(json: org.json.JSONObject) {
740
+ if (editorId == 0L) return
741
+ val driver = editorEditText.v2Driver ?: return
742
+ driver.setContentJson(json.toString())?.let {
743
+ editorEditText.applyUpdateJSON(
744
+ it,
745
+ notifyListener = false,
746
+ refreshInputConnectionForExternalUpdate = true
747
+ )
748
+ }
749
+ }
750
+
751
+ internal fun rebindEditorIfNeeded(notifyListener: Boolean = true) {
752
+ if (editorId != 0L && editorEditText.editorId != editorId) {
753
+ setEditorId(editorId, bindEditor = true, notifyListener = notifyListener)
754
+ }
755
+ }
756
+
757
+ internal fun setEditorIdWhileDetached(value: Long) {
758
+ setEditorId(value, bindEditor = false)
759
+ }
760
+
761
+ internal fun deferEditorUnbindOnNextDetach() {
762
+ deferEditorUnbindOnDetach = true
763
+ }
764
+
765
+ internal fun clearDeferredEditorUnbind() {
766
+ deferEditorUnbindOnDetach = false
767
+ }
768
+
769
+ internal fun unbindEditorForDetachedViewIfNeeded() {
770
+ if (isAttachedToWindow) return
771
+ deferEditorUnbindOnDetach = false
772
+ if (editorId != 0L) {
773
+ editorEditText.unbindEditor()
774
+ }
775
+ }
776
+
777
+ private fun setEditorId(value: Long, bindEditor: Boolean, notifyListener: Boolean = true) {
778
+ val targetBoundEditorId = if (bindEditor) value else 0L
779
+ if (currentEditorId == value && editorEditText.editorId == targetBoundEditorId) return
780
+ imageResizeOverlayView.cancelActiveResize()
781
+ if (currentEditorId != value || editorEditText.editorId != targetBoundEditorId) {
782
+ editorEditText.discardTransientNativeInputForEditorRebind()
783
+ }
784
+ invalidateAtomMeasurements(clearHeights = true)
785
+ currentEditorId = value
786
+ if (bindEditor && value != 0L) {
787
+ editorEditText.bindEditor(value, notifyListener = notifyListener)
788
+ } else {
789
+ editorEditText.unbindEditor()
790
+ }
791
+ refreshOverlays()
792
+ }
793
+
794
+ override fun onAttachedToWindow() {
795
+ super.onAttachedToWindow()
796
+ clearDeferredEditorUnbind()
797
+ rebindEditorIfNeeded()
798
+ atomHostViews.forEach { (key, child) -> scheduleAtomMeasurement(child, key) }
799
+ }
800
+
801
+ override fun onDetachedFromWindow() {
802
+ invalidateAtomMeasurements()
803
+ onBeforeDetachedFromWindow?.invoke()
804
+ imageResizeOverlayView.cancelActiveResize()
805
+ super.onDetachedFromWindow()
806
+ if (editorId != 0L && !deferEditorUnbindOnDetach) {
807
+ editorEditText.unbindEditor()
808
+ }
809
+ }
810
+
811
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
812
+ if (heightBehavior != EditorHeightBehavior.AUTO_GROW) {
813
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
814
+ return
815
+ }
816
+
817
+ val childWidthSpec = getChildMeasureSpec(
818
+ widthMeasureSpec,
819
+ paddingLeft + paddingRight,
820
+ editorViewport.layoutParams.width
821
+ )
822
+ val childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
823
+ editorViewport.measure(childWidthSpec, childHeightSpec)
824
+
825
+ val measuredWidth = resolveSize(
826
+ editorViewport.measuredWidth + paddingLeft + paddingRight,
827
+ widthMeasureSpec
828
+ )
829
+ val desiredHeight = editorViewport.measuredHeight + paddingTop + paddingBottom
830
+ val measuredHeight = when (MeasureSpec.getMode(heightMeasureSpec)) {
831
+ MeasureSpec.AT_MOST -> desiredHeight.coerceAtMost(
832
+ MeasureSpec.getSize(heightMeasureSpec)
833
+ )
834
+
835
+ else -> desiredHeight
836
+ }
837
+ setMeasuredDimension(measuredWidth, measuredHeight)
838
+ }
839
+
840
+ /** Fills a host-imposed floor so its extra space stays tappable. */
841
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
842
+ super.onLayout(changed, left, top, right, bottom)
843
+ if (heightBehavior == EditorHeightBehavior.AUTO_GROW) {
844
+ val available = (bottom - top) - paddingTop - paddingBottom
845
+ if (available > editorViewport.height) {
846
+ editorViewport.measure(
847
+ MeasureSpec.makeMeasureSpec(editorViewport.width, MeasureSpec.EXACTLY),
848
+ MeasureSpec.makeMeasureSpec(available, MeasureSpec.EXACTLY)
849
+ )
850
+ editorViewport.layout(
851
+ editorViewport.left,
852
+ paddingTop,
853
+ editorViewport.right,
854
+ paddingTop + available
855
+ )
856
+ }
857
+ }
858
+ layoutAtomHostViews()
859
+ }
860
+
861
+ private fun updateScrollContainerAppearance() {
862
+ val cornerRadiusPx = (theme?.borderRadius ?: 0f) * resources.displayMetrics.density
863
+ val backgroundColor = theme?.backgroundColor ?: baseBackgroundColor
864
+ editorViewport.background = GradientDrawable().apply {
865
+ cornerRadius = cornerRadiusPx
866
+ setColor(backgroundColor)
867
+ }
868
+ editorViewport.clipToOutline = cornerRadiusPx > 0f
869
+ editorScrollView.setBackgroundColor(Color.TRANSPARENT)
870
+ appliedCornerRadiusPx = cornerRadiusPx
871
+ appliedBackgroundColorForTesting = backgroundColor
872
+ }
873
+
874
+ private fun updateScrollContainerInsets() {
875
+ if (heightBehavior != EditorHeightBehavior.FIXED) {
876
+ editorScrollView.setPadding(0, 0, 0, 0)
877
+ return
878
+ }
879
+
880
+ val density = resources.displayMetrics.density
881
+ val topInset = ((theme?.contentInsets?.top ?: 0f) * density).toInt()
882
+ val bottomInset = ((theme?.contentInsets?.bottom ?: 0f) * density).toInt()
883
+ editorScrollView.setPadding(0, topInset, 0, bottomInset + viewportBottomInsetPx)
884
+ }
885
+
886
+ private fun createContainerLayoutParams(): LayoutParams =
887
+ if (heightBehavior == EditorHeightBehavior.AUTO_GROW) {
888
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
889
+ } else {
890
+ LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f)
891
+ }
892
+
893
+ private fun createEditorLayoutParams(): FrameLayout.LayoutParams = FrameLayout.LayoutParams(
894
+ ViewGroup.LayoutParams.MATCH_PARENT,
895
+ ViewGroup.LayoutParams.MATCH_PARENT
896
+ )
897
+
898
+ internal fun selectedImageGeometry(): EditorEditText.SelectedImageGeometry? {
899
+ val geometry = editorEditText.selectedImageGeometry() ?: return null
900
+ val editorOrigin = Rect()
901
+ editorViewport.offsetDescendantRectToMyCoords(editorEditText, editorOrigin)
902
+ return EditorEditText.SelectedImageGeometry(
903
+ docPos = geometry.docPos,
904
+ rect = RectF(geometry.rect).apply {
905
+ offset(editorOrigin.left.toFloat(), editorOrigin.top.toFloat())
906
+ }
907
+ )
908
+ }
909
+
910
+ internal fun caretRect(): RectF? {
911
+ val rect = editorEditText.caretRect() ?: return null
912
+ return RectF(
913
+ editorViewport.left + editorScrollView.left + editorEditText.left + rect.left,
914
+ editorViewport.top + editorScrollView.top + editorEditText.top + rect.top -
915
+ editorScrollView.scrollY,
916
+ editorViewport.left + editorScrollView.left + editorEditText.left + rect.right,
917
+ editorViewport.top + editorScrollView.top + editorEditText.top + rect.bottom -
918
+ editorScrollView.scrollY
919
+ )
920
+ }
921
+
922
+ internal fun maximumImageWidthPx(): Float {
923
+ val availableWidth =
924
+ maxOf(editorEditText.width, editorEditText.measuredWidth) -
925
+ editorEditText.compoundPaddingLeft -
926
+ editorEditText.compoundPaddingRight
927
+ return availableWidth.coerceAtLeast(48).toFloat()
928
+ }
929
+
930
+ internal fun clampImageSize(
931
+ widthPx: Float,
932
+ heightPx: Float,
933
+ maximumWidthPx: Float = maximumImageWidthPx()
934
+ ): Pair<Float, Float> {
935
+ val aspectRatio = maxOf(widthPx / maxOf(heightPx, 1f), 0.1f)
936
+ val clampedWidth = minOf(maxOf(48f, maximumWidthPx), maxOf(48f, widthPx))
937
+ val clampedHeight = maxOf(48f, clampedWidth / aspectRatio)
938
+ return clampedWidth to clampedHeight
939
+ }
940
+
941
+ internal fun resizeImage(docPos: Int, widthPx: Float, heightPx: Float) {
942
+ val (clampedWidth, clampedHeight) = clampImageSize(widthPx, heightPx)
943
+ editorEditText.resizeImageAtDocPos(docPos, clampedWidth, clampedHeight)
944
+ }
945
+
946
+ private fun refreshOverlays() {
947
+ layoutAtomHostViews()
948
+ remoteSelectionOverlayView.refreshGeometry()
949
+ imageResizeOverlayView.refresh()
950
+ }
951
+ }