@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,900 @@
1
+ package com.apollohg.editor.viewer
2
+
3
+ import android.content.Context
4
+ import android.graphics.Rect
5
+ import com.apollohg.editor.ImageLoadingPolicy
6
+ import com.apollohg.editor.ProseViewerConfiguration
7
+ import com.apollohg.editor.ProseViewerError
8
+ import com.apollohg.editor.ProseViewerSource
9
+ import com.facebook.react.bridge.Arguments
10
+ import com.facebook.react.bridge.ReadableMap
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.facebook.react.module.annotations.ReactModule
13
+ import com.facebook.react.uimanager.ReactStylesDiffMap
14
+ import com.facebook.react.uimanager.SimpleViewManager
15
+ import com.facebook.react.uimanager.StateWrapper
16
+ import com.facebook.react.uimanager.ThemedReactContext
17
+ import com.facebook.react.uimanager.UIManagerHelper
18
+ import com.facebook.react.uimanager.ViewManagerDelegate
19
+ import com.facebook.react.uimanager.events.Event
20
+ import com.facebook.react.viewmanagers.PreparedProseViewerManagerDelegate
21
+ import com.facebook.react.viewmanagers.PreparedProseViewerManagerInterface
22
+ import com.facebook.yoga.YogaMeasureMode
23
+ import com.facebook.yoga.YogaMeasureOutput
24
+ import java.util.WeakHashMap
25
+ import kotlin.math.roundToInt
26
+
27
+ internal fun fabricConstraintPixels(value: Float): Int? {
28
+ if (!value.isFinite() || value <= 0f || value > Int.MAX_VALUE.toFloat()) return null
29
+ return value.roundToInt().takeIf { it > 0 }
30
+ }
31
+
32
+ internal fun fabricPixelsToDp(value: Float, density: Float): Float? {
33
+ if (!value.isFinite() || value < 0f || !density.isFinite() || density <= 0f) return null
34
+ return value / density
35
+ }
36
+
37
+ /** Fabric ViewManager; Yoga measurement creates the artifact and mounting only acquires it. */
38
+ @ReactModule(name = PreparedProseViewerManager.REACT_CLASS)
39
+ internal class PreparedProseViewerManager :
40
+ SimpleViewManager<PreparedProseDrawingView>(),
41
+ PreparedProseViewerManagerInterface<PreparedProseDrawingView> {
42
+ private val delegate: ViewManagerDelegate<PreparedProseDrawingView> =
43
+ PreparedProseViewerManagerDelegate(this)
44
+ private val states = WeakHashMap<PreparedProseDrawingView, ViewState>()
45
+
46
+ override fun getName(): String = REACT_CLASS
47
+
48
+ override fun getDelegate(): ViewManagerDelegate<PreparedProseDrawingView> = delegate
49
+
50
+ override fun createViewInstance(context: ThemedReactContext): PreparedProseDrawingView =
51
+ PreparedProseDrawingView(context).also { view ->
52
+ val state = ViewState()
53
+ states[view] = state
54
+ view.onCodeHighlightsReady = { state.publishFontRevision(1) }
55
+ view.onUsableMetricsChanged = { installCachedLayout(view) }
56
+ view.onVisibleRectChanged = { visible -> state.requestVisibleImages(view, visible) }
57
+ view.onFontConfigurationChanged =
58
+ { configuration -> state.fontEnvironment.onConfigurationChanged(configuration) }
59
+ view.onInteractionActivated = { interaction -> dispatchInteraction(view, interaction) }
60
+ state.fontEnvironment.onInvalidated =
61
+ { revision -> state.publishFontRevision(revision) }
62
+ state.fontEnvironment.activate(deliverPending = true)
63
+ state.imagePipeline.onPixels = { attachment, lease ->
64
+ val request = state.requestOrNull()
65
+ if (request != null &&
66
+ state.imagePipeline.acceptsCompletion(request.semanticGenerationIdentity)
67
+ ) {
68
+ view.putImageLease(attachment.id, lease)
69
+ } else {
70
+ lease.close()
71
+ }
72
+ }
73
+ state.imagePipeline.onPixelsReleased = view::removeImageLeases
74
+ state.imagePipeline.onIntrinsicMetadata = { attachment, width, height ->
75
+ if (state.recordIntrinsicSize(attachment, width, height)) {
76
+ state.publishAttachmentRevision()
77
+ }
78
+ }
79
+ state.imagePipeline.onResourceFailure =
80
+ { attachment -> dispatchResourceError(view, attachment) }
81
+ }
82
+
83
+ override fun onDropViewInstance(view: PreparedProseDrawingView) {
84
+ states.remove(view)?.let { state ->
85
+ state.finishWithoutMountedReplacement(view)
86
+ state.release()
87
+ }
88
+ view.onCodeHighlightsReady = null
89
+ view.onUsableMetricsChanged = null
90
+ view.onVisibleRectChanged = null
91
+ view.onFontConfigurationChanged = null
92
+ view.onInteractionActivated = null
93
+ view.clearImageLeases()
94
+ view.install(null)
95
+ super.onDropViewInstance(view)
96
+ }
97
+
98
+ override fun onSurfaceStopped(surfaceId: Int) {
99
+ // Yoga can measure a component that never receives a mounted View, so
100
+ // the weak view map is only supplemental lifecycle bookkeeping. The
101
+ // registry cleanup is intentionally unconditional and surface-wide.
102
+ PreparedProseLayoutRegistry.shared.deactivateFabricSurfaceId(surfaceId)
103
+ states.entries.forEach { (view, state) ->
104
+ if (state.ownsFabricSurface(surfaceId)) {
105
+ state.release()
106
+ view.clearImageLeases()
107
+ view.install(null)
108
+ }
109
+ }
110
+ super.onSurfaceStopped(surfaceId)
111
+ }
112
+
113
+ /** Test-only Fabric-side per-surface accounting; cache bytes stay separate. */
114
+ internal fun retainedSurfaceBytesForTesting(view: PreparedProseDrawingView): Long =
115
+ states[view]?.retainedSurfaceBytesForTesting(view)
116
+ ?: PreparedProseDrawingView.retainedImagePixelsBytes(emptyMap<String, Any>())
117
+
118
+ override fun updateState(
119
+ view: PreparedProseDrawingView,
120
+ props: ReactStylesDiffMap,
121
+ stateWrapper: StateWrapper
122
+ ): Any? {
123
+ val state = states.getOrPut(view, ::ViewState)
124
+ state.fontEnvironment.activate(deliverPending = true)
125
+ state.replaceStateWrapper(stateWrapper, stateWrapper.stateData?.fabricRevisionsOrNull())
126
+ state.beginSemanticImageGeneration(view)
127
+ reconcile(view, state)
128
+ return null
129
+ }
130
+
131
+ override fun setSourceKind(view: PreparedProseDrawingView, value: String?) =
132
+ update(view) { sourceKind = value ?: "json" }
133
+
134
+ override fun setSource(view: PreparedProseDrawingView, value: String?) =
135
+ update(view) { source = value.orEmpty() }
136
+
137
+ override fun setConfigJson(view: PreparedProseDrawingView, value: String?) =
138
+ update(view) { configJson = value ?: "{}" }
139
+
140
+ override fun setThemeJson(view: PreparedProseDrawingView, value: String?) =
141
+ update(view) { themeJson = value }
142
+
143
+ override fun setImagePolicyJson(view: PreparedProseDrawingView, value: String?) =
144
+ update(view) { imagePolicyJson = value }
145
+
146
+ override fun setImagesEnabled(view: PreparedProseDrawingView, value: Boolean) =
147
+ update(view) { imagesEnabled = value }
148
+
149
+ override fun setCollapsesWhenEmpty(view: PreparedProseDrawingView, value: Boolean) =
150
+ update(view) { collapsesWhenEmpty = value }
151
+
152
+ override fun setEnableLinkTaps(view: PreparedProseDrawingView, value: Boolean) {
153
+ // Capability only filters the installed interaction/accessibility
154
+ // nodes. It must not reconcile, acquire, or replace the generation.
155
+ view.linkInteractionsEnabled = value
156
+ }
157
+
158
+ override fun setMentionInteractionsEnabled(view: PreparedProseDrawingView, value: Boolean) {
159
+ view.mentionInteractionsEnabled = value
160
+ }
161
+
162
+ override fun setFontEnvironmentRevision(view: PreparedProseDrawingView, value: Int) =
163
+ update(view) { fontEnvironmentRevision = value.coerceAtLeast(0).toLong() }
164
+
165
+ override fun onAfterUpdateTransaction(view: PreparedProseDrawingView) {
166
+ super.onAfterUpdateTransaction(view)
167
+ val state = states[view] ?: return
168
+ state.beginSemanticImageGeneration(view)
169
+ reconcile(view, state)
170
+ }
171
+
172
+ override fun measure(
173
+ context: Context,
174
+ localData: ReadableMap?,
175
+ props: ReadableMap?,
176
+ state: ReadableMap?,
177
+ width: Float,
178
+ widthMode: YogaMeasureMode,
179
+ height: Float,
180
+ heightMode: YogaMeasureMode,
181
+ attachmentsPositions: FloatArray?
182
+ ): Long {
183
+ val density = context.resources.displayMetrics.density
184
+ val fontScale = context.resources.configuration.fontScale
185
+ val surface = localData?.let(::surfaceToken)
186
+ val finalLayout = FabricLeaseHandleBridge.currentFinalLayout()
187
+ val leaseHandle = FabricLeaseHandleBridge.currentHandle()
188
+ // A state snapshot can outlive its C++ family. Only the synchronous
189
+ // native thread-local handle proves this Yoga callback still belongs
190
+ // to an active exact state incarnation.
191
+ if (surface != null && leaseHandle <= 0L) {
192
+ PreparedProseInstrumentation.trace("measure") {
193
+ "declined: $surface has no live native lease handle"
194
+ }
195
+ return YogaMeasureOutput.make(0f, 0f)
196
+ }
197
+ val request = requestFrom(props, state, leaseHandle)
198
+ if (request == null) {
199
+ // An incomplete/stale Yoga callback has not named an exact
200
+ // generation. It must never tear down a newer incarnation on the
201
+ // same surface; recycle and surface-stop own terminal cleanup.
202
+ PreparedProseInstrumentation.trace("measure") {
203
+ "declined: state named no exact generation (surface=$surface, handle=$leaseHandle)"
204
+ }
205
+ return YogaMeasureOutput.make(0f, 0f)
206
+ }
207
+ val widthPx = fabricConstraintPixels(width)
208
+ // The registry begins and scopes this exact surface/component sidecar
209
+ // around preparation, so an LRU miss cannot borrow another surface.
210
+ val artifact = if (finalLayout != null && surface != null && widthPx != null) {
211
+ PreparedProseLayoutRegistry.shared.prepareFinalLayout(
212
+ request,
213
+ widthPx,
214
+ density,
215
+ finalLayout.contentOriginXPx,
216
+ finalLayout.contentOriginYPx,
217
+ surface,
218
+ leaseHandle,
219
+ fontScale
220
+ )
221
+ } else if (
222
+ (widthMode != YogaMeasureMode.EXACTLY && widthMode != YogaMeasureMode.AT_MOST) ||
223
+ widthPx == null
224
+ ) {
225
+ PreparedProseLayoutRegistry.shared.measure(
226
+ request,
227
+ 0,
228
+ density,
229
+ fabricSurface = surface,
230
+ fabricLeaseHandle = leaseHandle,
231
+ fontScale = fontScale
232
+ )
233
+ } else {
234
+ PreparedProseLayoutRegistry.shared.measure(
235
+ request,
236
+ widthPx,
237
+ density,
238
+ fabricSurface = surface,
239
+ fabricLeaseHandle = leaseHandle,
240
+ fontScale = fontScale
241
+ )
242
+ }
243
+ val measuredWidth = fabricPixelsToDp(artifact.widthPx.toFloat(), density) ?: 0f
244
+ val intrinsicHeight = fabricPixelsToDp(artifact.heightPx.toFloat(), density) ?: 0f
245
+ val constrainedHeight = fabricPixelsToDp(height, density)
246
+ val measuredHeight = when (heightMode) {
247
+ YogaMeasureMode.EXACTLY -> constrainedHeight ?: 0f
248
+
249
+ YogaMeasureMode.AT_MOST -> constrainedHeight?.let { minOf(intrinsicHeight, it) }
250
+ ?: intrinsicHeight
251
+
252
+ else -> intrinsicHeight
253
+ }
254
+ PreparedProseInstrumentation.trace("measure") {
255
+ "surface=$surface handle=$leaseHandle $widthMode widthPx=$widthPx " +
256
+ "$heightMode heightPx=$height " +
257
+ "-> artifact ${artifact.widthPx}x${artifact.heightPx}px, yoga ${measuredWidth}x${measuredHeight}dp"
258
+ }
259
+ return YogaMeasureOutput.make(measuredWidth, measuredHeight)
260
+ }
261
+
262
+ private fun update(view: PreparedProseDrawingView, mutation: ViewState.() -> Unit) {
263
+ val state = states.getOrPut(view, ::ViewState)
264
+ state.mutation()
265
+ }
266
+
267
+ private fun installCachedLayout(view: PreparedProseDrawingView) {
268
+ val state = states[view] ?: run {
269
+ PreparedProseInstrumentation.trace("mount") {
270
+ "declined: no view state (tag=${view.id})"
271
+ }
272
+ return
273
+ }
274
+ val request = state.requestOrNull() ?: run {
275
+ PreparedProseInstrumentation.trace("mount") {
276
+ "declined: props/state have not yet named a request (tag=${view.id})"
277
+ }
278
+ return
279
+ }
280
+ val surfaceId = UIManagerHelper.getSurfaceId(view)
281
+ if (surfaceId < 0 || view.id <= 0) {
282
+ PreparedProseInstrumentation.trace("mount") {
283
+ "declined: surfaceId=$surfaceId tag=${view.id}"
284
+ }
285
+ state.finishWithoutMountedReplacement(view)
286
+ return
287
+ }
288
+ val surface = FabricSurfaceToken(surfaceId, view.id)
289
+ val generation = state.adopt(surface, request)
290
+ state.bindFabricAttachmentState(generation)
291
+ val ticket = PreparedProseLayoutRegistry.shared.acquirePreparedMountTicket(
292
+ generation,
293
+ request.nativeFontRevision
294
+ )
295
+ if (ticket == null) {
296
+ PreparedProseInstrumentation.trace("mount") {
297
+ "miss: no final layout ticket for $generation"
298
+ }
299
+ PreparedProseLayoutRegistry.shared.prepareForFabricMount(generation) {
300
+ view.post {
301
+ val current = states[view] ?: return@post
302
+ if (current.generation != generation) return@post
303
+ val currentRequest = current.requestOrNull() ?: return@post
304
+ val prepared = PreparedProseLayoutRegistry.shared.acquirePreparedMountTicket(
305
+ generation,
306
+ currentRequest.nativeFontRevision
307
+ )
308
+ ?: return@post
309
+ installPreparedTicket(view, current, prepared)
310
+ }
311
+ }
312
+ return
313
+ }
314
+ installPreparedTicket(view, state, ticket)
315
+ }
316
+
317
+ private fun installPreparedTicket(
318
+ view: PreparedProseDrawingView,
319
+ state: ViewState,
320
+ ticket: PreparedMountTicket
321
+ ) {
322
+ val currentRequest = state.requestOrNull() ?: return
323
+ if (ticket.nativeFontRevision != currentRequest.nativeFontRevision) {
324
+ prepareCurrentTicket(view, state)
325
+ return
326
+ }
327
+ PreparedProseInstrumentation.trace("mount") {
328
+ "installed: ${ticket.generation} widthPx=${ticket.contentWidthPx} heightPx=${ticket.artifact.heightPx}"
329
+ }
330
+ state.installMountedReplacement(view, ticket)
331
+ state.beginImages(view, ticket.artifact, currentRequest)
332
+ ViewerAtomConfiguration.parse(currentRequest.configuration.themeJson)?.let { atoms ->
333
+ val density = view.resources.displayMetrics.density
334
+ dispatchViewerEvent(
335
+ view,
336
+ "topAtomLayout",
337
+ Arguments.createMap().apply {
338
+ putString("generation", atoms.generation)
339
+ putString("revision", atoms.revision)
340
+ putDouble("layoutWidth", ticket.artifact.widthPx.toDouble() / density)
341
+ putString(
342
+ "atomsJson",
343
+ ticket.artifact.atomsJson(
344
+ density,
345
+ ticket.contentOriginXPx,
346
+ ticket.contentOriginYPx
347
+ )
348
+ )
349
+ }
350
+ )
351
+ }
352
+ ticket.artifact.error?.let { dispatchError(view, currentRequest, it) }
353
+ }
354
+
355
+ private fun prepareCurrentTicket(view: PreparedProseDrawingView, state: ViewState) {
356
+ val generation = state.generation ?: return
357
+ PreparedProseLayoutRegistry.shared.prepareForFabricMount(generation) { prepared ->
358
+ if (!prepared) return@prepareForFabricMount
359
+ view.post {
360
+ val current = states[view] ?: return@post
361
+ if (current.generation != generation) return@post
362
+ val request = current.requestOrNull() ?: return@post
363
+ val ticket = PreparedProseLayoutRegistry.shared.acquirePreparedMountTicket(
364
+ generation,
365
+ request.nativeFontRevision
366
+ )
367
+ ?: return@post
368
+ installPreparedTicket(view, current, ticket)
369
+ }
370
+ }
371
+ }
372
+
373
+ private fun dispatchError(
374
+ view: PreparedProseDrawingView,
375
+ request: ProseViewerRequest,
376
+ error: ProseViewerError
377
+ ) {
378
+ val state = states[view] ?: return
379
+ if (!state.errorReporter.shouldReport(request.semanticGenerationIdentity)) return
380
+ dispatchViewerEvent(
381
+ view,
382
+ EVENT_ERROR,
383
+ Arguments.createMap().apply {
384
+ putString("domain", error.domain)
385
+ putString("code", error.code.value)
386
+ putString("message", error.message)
387
+ putBoolean("fatal", true)
388
+ }
389
+ )
390
+ }
391
+
392
+ private fun dispatchResourceError(
393
+ view: PreparedProseDrawingView,
394
+ attachment: ViewerImageAttachment
395
+ ) {
396
+ val state = states[view] ?: return
397
+ state.requestOrNull() ?: return
398
+ if (!state.recordResourceFailure(attachment.ordinal)) return
399
+ dispatchViewerEvent(
400
+ view,
401
+ EVENT_ERROR,
402
+ Arguments.createMap().apply {
403
+ putString("domain", "viewer.resource")
404
+ putString("code", "RESOURCE_LOAD_FAILED")
405
+ putString("message", "An image resource could not be loaded.")
406
+ putBoolean("fatal", false)
407
+ }
408
+ )
409
+ }
410
+
411
+ private fun dispatchInteraction(
412
+ view: PreparedProseDrawingView,
413
+ interaction: PreparedProseInteraction
414
+ ): Boolean {
415
+ val isLink = interaction.kind == PreparedProseInteraction.Kind.LINK
416
+ val payload = Arguments.createMap().apply {
417
+ if (isLink) {
418
+ putString("href", interaction.href)
419
+ putString("text", interaction.visibleText)
420
+ } else {
421
+ putDouble("docPos", (interaction.docPos ?: return false).toDouble())
422
+ putString("label", interaction.label)
423
+ putString("attrsJson", interaction.attrsJson ?: return false)
424
+ }
425
+ }
426
+ dispatchViewerEvent(view, if (isLink) EVENT_PRESS_LINK else EVENT_PRESS_MENTION, payload)
427
+ return true
428
+ }
429
+
430
+ private fun dispatchViewerEvent(
431
+ view: PreparedProseDrawingView,
432
+ eventName: String,
433
+ payload: WritableMap
434
+ ) {
435
+ val context = UIManagerHelper.getReactContext(view)
436
+ UIManagerHelper.getEventDispatcherForReactTag(context, view.id)?.dispatchEvent(
437
+ PreparedProseViewerEvent(
438
+ UIManagerHelper.getSurfaceId(view),
439
+ view.id,
440
+ eventName,
441
+ payload
442
+ )
443
+ )
444
+ }
445
+
446
+ private fun requestFrom(
447
+ props: ReadableMap?,
448
+ state: ReadableMap?,
449
+ nativeLeaseHandle: Long = 0
450
+ ): ProseViewerRequest? {
451
+ val revisions = state?.fabricRevisionsOrNull(nativeLeaseHandle) ?: return null
452
+ return requestFrom(props, revisions)
453
+ }
454
+
455
+ private fun requestFrom(
456
+ props: ReadableMap?,
457
+ revisions: FabricStateRevisions
458
+ ): ProseViewerRequest {
459
+ val sourceKind = props?.stringOrNull("sourceKind") ?: "json"
460
+ val source = if (sourceKind == "html") {
461
+ ProseViewerSource.Html(props?.stringOrNull("source").orEmpty())
462
+ } else {
463
+ ProseViewerSource.Json(props?.stringOrNull("source").orEmpty())
464
+ }
465
+ return ProseViewerRequest(
466
+ source = source,
467
+ configuration = ProseViewerConfiguration(
468
+ configJson = props?.stringOrNull("configJson") ?: "{}",
469
+ themeJson = props?.stringOrNull("themeJson"),
470
+ imagePolicyJson = props?.stringOrNull("imagePolicyJson"),
471
+ imagesEnabled = props?.booleanOrDefault("imagesEnabled", true) ?: true,
472
+ collapsesWhenEmpty = props?.booleanOrDefault("collapsesWhenEmpty", true) ?: true
473
+ ),
474
+ attachmentRevision = revisions.attachmentRevision,
475
+ nativeFontRevision = revisions.nativeFontRevision,
476
+ fontEnvironmentRevision = props?.longOrZero("fontEnvironmentRevision") ?: 0
477
+ )
478
+ }
479
+
480
+ private fun reconcile(view: PreparedProseDrawingView, state: ViewState) {
481
+ val request = state.requestOrNull() ?: run {
482
+ state.releaseGeneration(view)
483
+ return
484
+ }
485
+ // `adopt` is the authoritative commit boundary. Releasing G1 here
486
+ // would race a G2 Yoga handoff and blank a valid mounted fallback.
487
+ installCachedLayout(view)
488
+ }
489
+
490
+ private fun surfaceToken(data: ReadableMap): FabricSurfaceToken? {
491
+ val surfaceId = data.longOrZero("surfaceId").toInt()
492
+ val componentTag = data.longOrZero("componentTag").toInt()
493
+ return if (surfaceId > 0 &&
494
+ componentTag > 0
495
+ ) {
496
+ FabricSurfaceToken(surfaceId, componentTag)
497
+ } else {
498
+ null
499
+ }
500
+ }
501
+
502
+ private fun ReadableMap.stringOrNull(key: String): String? =
503
+ if (hasKey(key) && !isNull(key)) getString(key) else null
504
+
505
+ private fun ReadableMap.booleanOrDefault(key: String, default: Boolean): Boolean =
506
+ if (hasKey(key) && !isNull(key)) getBoolean(key) else default
507
+
508
+ private fun ReadableMap.longOrZero(key: String): Long =
509
+ if (!hasKey(key) || isNull(key)) 0 else getDouble(key).toLong().coerceAtLeast(0)
510
+
511
+ private fun ReadableMap.fabricRevisionsOrNull(
512
+ nativeLeaseHandle: Long = 0
513
+ ): FabricStateRevisions? {
514
+ val attachmentRevision = longOrNull("attachmentRevision") ?: return null
515
+ val nativeFontRevision = longOrNull("nativeFontRevision") ?: return null
516
+ // State's optional decimal representation is exact. The hot native
517
+ // measurement path receives this same handle as a JNI jlong above.
518
+ val stateHandle = stringOrNull("leaseHandle")?.toLongOrNull()?.takeIf { it > 0 } ?: 0L
519
+ val handle = stateHandle.takeIf { it > 0 } ?: nativeLeaseHandle
520
+ if (nativeLeaseHandle > 0 && stateHandle > 0 &&
521
+ stateHandle != nativeLeaseHandle
522
+ ) {
523
+ return null
524
+ }
525
+ return FabricStateRevisions(attachmentRevision, nativeFontRevision, handle)
526
+ }
527
+
528
+ private fun ReadableMap.longOrNull(key: String): Long? =
529
+ if (!hasKey(key) || isNull(key)) null else getDouble(key).toLong().coerceAtLeast(0)
530
+
531
+ internal data class FabricStateRevisions(
532
+ val attachmentRevision: Long,
533
+ val nativeFontRevision: Long,
534
+ val leaseHandle: Long
535
+ )
536
+
537
+ internal class ViewState(
538
+ var sourceKind: String = "json",
539
+ var source: String = "",
540
+ var configJson: String = "{}",
541
+ var themeJson: String? = null,
542
+ var imagePolicyJson: String? = null,
543
+ var imagesEnabled: Boolean = true,
544
+ var collapsesWhenEmpty: Boolean = true,
545
+ var fontEnvironmentRevision: Long = 0,
546
+ var revisions: FabricStateRevisions? = null,
547
+ var stateWrapper: StateWrapper? = null,
548
+ var generation: FabricGenerationToken? = null,
549
+ // This is independent from the generation lease: an accepted props
550
+ // replacement can release the lease before the next Yoga pass, while
551
+ // the mounted sidecar is still owned by this exact Fabric token.
552
+ private var sidecarGeneration: FabricGenerationToken? = null,
553
+ val errorReporter: FabricErrorReporter = FabricErrorReporter(),
554
+ private val replacementAccessibilityTransaction: FabricReplacementAccessibilityTransaction =
555
+ FabricReplacementAccessibilityTransaction(),
556
+ private val createStateMap: () -> WritableMap = { Arguments.createMap() }
557
+ ) {
558
+ private var pendingFontRevision = 0L
559
+ fun requestOrNull(): ProseViewerRequest? = revisions?.let { revisions ->
560
+ ProseViewerRequest(
561
+ source = if (sourceKind ==
562
+ "html"
563
+ ) {
564
+ ProseViewerSource.Html(source)
565
+ } else {
566
+ ProseViewerSource.Json(source)
567
+ },
568
+ configuration = ProseViewerConfiguration(
569
+ configJson,
570
+ themeJson,
571
+ imagePolicyJson,
572
+ imagesEnabled,
573
+ collapsesWhenEmpty
574
+ ),
575
+ attachmentRevision = revisions.attachmentRevision,
576
+ nativeFontRevision = revisions.nativeFontRevision,
577
+ fontEnvironmentRevision = fontEnvironmentRevision
578
+ )
579
+ }
580
+
581
+ fun replaceStateWrapper(next: StateWrapper, nextRevisions: FabricStateRevisions?) {
582
+ if (stateWrapper !== next) stateWrapper?.destroyState()
583
+ stateWrapper = next
584
+ val prior = revisions
585
+ revisions = nextRevisions?.let { incoming ->
586
+ val leaseHandle = incoming.leaseHandle.takeIf { it > 0 }
587
+ ?: prior?.leaseHandle
588
+ ?: 0
589
+ if (leaseHandle > 0 && prior?.leaseHandle == leaseHandle) {
590
+ incoming.copy(
591
+ attachmentRevision = maxOf(
592
+ incoming.attachmentRevision,
593
+ prior.attachmentRevision
594
+ ),
595
+ nativeFontRevision = maxOf(
596
+ incoming.nativeFontRevision,
597
+ prior.nativeFontRevision
598
+ ),
599
+ leaseHandle = leaseHandle
600
+ )
601
+ } else {
602
+ incoming.copy(leaseHandle = leaseHandle)
603
+ }
604
+ }
605
+ if (pendingFontRevision > 0 && revisions != null) {
606
+ pendingFontRevision = 0
607
+ val current = requireNotNull(revisions)
608
+ publishRevisions(
609
+ FabricStateRevisions(
610
+ current.attachmentRevision,
611
+ current.nativeFontRevision + 1,
612
+ current.leaseHandle
613
+ )
614
+ )
615
+ }
616
+ }
617
+
618
+ private var attachmentRevisions = ViewerAttachmentRevisionState()
619
+ val fontEnvironment = ViewerFontEnvironment()
620
+ val imagePipeline = ViewerImagePipeline()
621
+ private var visibleRect: android.graphics.Rect = android.graphics.Rect()
622
+
623
+ fun beginImages(
624
+ view: PreparedProseDrawingView,
625
+ artifact: PreparedProseLayout,
626
+ request: ProseViewerRequest
627
+ ) {
628
+ // The semantic reset happens when props/state are accepted, before
629
+ // Yoga can prepare a replacement. Mount binds only this artifact's
630
+ // ordinal descriptors and must preserve a permitted revision.
631
+ attachmentRevisions.admit(artifact.imageAttachments.size)
632
+ fontEnvironment.activate()
633
+ imagePipeline.begin(
634
+ request.semanticGenerationIdentity,
635
+ request.configuration.imagesEnabled,
636
+ ImageLoadingPolicy.fromJson(request.configuration.imagePolicyJson)
637
+ )
638
+ }
639
+
640
+ /** Phase one of Fabric image setup: no artifact is required yet. */
641
+ fun beginSemanticImageGeneration(view: PreparedProseDrawingView) {
642
+ val request = requestOrNull() ?: return
643
+ if (!attachmentRevisions.beginSemanticGeneration(
644
+ request.semanticGenerationIdentity
645
+ )
646
+ ) {
647
+ return
648
+ }
649
+ imagePipeline.cancel()
650
+ view.clearImageLeases()
651
+ }
652
+
653
+ fun bindFabricAttachmentState(generation: FabricGenerationToken) {
654
+ attachmentRevisions = FabricAttachmentSidecars.state(generation) ?: attachmentRevisions
655
+ }
656
+
657
+ fun recordIntrinsicSize(
658
+ attachment: ViewerImageAttachment,
659
+ width: Int,
660
+ height: Int
661
+ ): Boolean = attachmentRevisions.recordIntrinsicSize(
662
+ attachment.id,
663
+ attachment.ordinal,
664
+ width,
665
+ height,
666
+ attachment.declaredSize
667
+ )
668
+
669
+ fun recordResourceFailure(ordinal: Int): Boolean =
670
+ attachmentRevisions.recordResourceFailure(ordinal)
671
+
672
+ internal fun retainedSurfaceBytesForTesting(view: PreparedProseDrawingView): Long {
673
+ val layout = view.preparedLayout?.retainedBytes ?: 0L
674
+ val sidecar = attachmentRevisions.retainedPublicationBytesForTesting.toLong()
675
+ val pixels = view.retainedImagePixelsBytesForTesting
676
+ return listOf(layout, sidecar, pixels).fold(0L) { total, value ->
677
+ if (value > 0 && total > Long.MAX_VALUE - value) Long.MAX_VALUE else total + value
678
+ }
679
+ }
680
+
681
+ fun requestVisibleImages(view: PreparedProseDrawingView, visible: android.graphics.Rect) {
682
+ visibleRect = android.graphics.Rect(visible)
683
+ val artifact = view.preparedLayout ?: return
684
+ imagePipeline.updateVisibleRect(visibleRect, artifact.imageAttachments)
685
+ }
686
+
687
+ fun publishAttachmentRevision() {
688
+ val current = revisions ?: return
689
+ publishRevisions(
690
+ FabricStateRevisions(
691
+ current.attachmentRevision + 1,
692
+ current.nativeFontRevision,
693
+ current.leaseHandle
694
+ )
695
+ )
696
+ }
697
+
698
+ fun publishFontRevision(revision: Long) {
699
+ if (revision <= 0) return
700
+ val current = revisions
701
+ if (current == null) {
702
+ pendingFontRevision = maxOf(pendingFontRevision, revision)
703
+ return
704
+ }
705
+ publishRevisions(
706
+ FabricStateRevisions(
707
+ current.attachmentRevision,
708
+ current.nativeFontRevision + 1,
709
+ current.leaseHandle
710
+ )
711
+ )
712
+ }
713
+
714
+ private fun publishRevisions(next: FabricStateRevisions) {
715
+ val current = revisions
716
+ if (current == next) return
717
+ revisions = next
718
+ stateWrapper?.updateState(
719
+ createStateMap().apply {
720
+ putDouble("attachmentRevision", next.attachmentRevision.toDouble())
721
+ putDouble("nativeFontRevision", next.nativeFontRevision.toDouble())
722
+ }
723
+ )
724
+ }
725
+
726
+ fun releaseGeneration(view: PreparedProseDrawingView) {
727
+ generation?.let(PreparedProseLayoutRegistry.shared::releaseFabricGeneration)
728
+ generation = null
729
+ releaseSidecarOwnership()
730
+ imagePipeline.cancel()
731
+ replacementAccessibilityTransaction.finishWithoutMountedReplacement(view)
732
+ view.clearImageLeases()
733
+ view.install(null)
734
+ }
735
+
736
+ fun installMountedReplacement(view: PreparedProseDrawingView, ticket: PreparedMountTicket) {
737
+ replacementAccessibilityTransaction.installMountedReplacement(view, ticket)
738
+ }
739
+
740
+ fun finishWithoutMountedReplacement(view: PreparedProseDrawingView) {
741
+ replacementAccessibilityTransaction.finishWithoutMountedReplacement(view)
742
+ }
743
+
744
+ fun adopt(surface: FabricSurfaceToken, request: ProseViewerRequest): FabricGenerationToken {
745
+ val handle = revisions?.leaseHandle ?: 0L
746
+ require(handle > 0) { "Fabric mount attempted without an exact native lease handle." }
747
+ val next = FabricGenerationToken(surface, request.generationIdentity, handle)
748
+ // This is the state/props commit boundary. Permit the canonical
749
+ // incoming generation before releasing any prior owner or trying
750
+ // to acquire: delayed G1 Yoga callbacks are now inert, while a
751
+ // G2 measurement already in flight remains eligible to finish.
752
+ PreparedProseLayoutRegistry.shared.activateFabricGeneration(next)
753
+ val previousSidecar = sidecarGeneration
754
+ if (previousSidecar != null && previousSidecar != next) {
755
+ // The view may already carry another Fabric tag by the time
756
+ // this callback runs. Remove only the sidecar token recorded
757
+ // for its former owner, never one reconstructed from the view.
758
+ PreparedProseLayoutRegistry.shared.releaseFabricGeneration(previousSidecar)
759
+ }
760
+ sidecarGeneration = next
761
+ if (generation != null && generation != next) {
762
+ PreparedProseLayoutRegistry.shared.releaseFabricGeneration(generation!!)
763
+ }
764
+ generation = next
765
+ return next
766
+ }
767
+
768
+ fun release() {
769
+ // A replacement keeps mounted G1 alive while G2 is pending or
770
+ // failed. Recycling is terminal for the exact state-family
771
+ // handle, so sweep every owner lease after marking it inactive
772
+ // rather than releasing only G2 through active-generation checks.
773
+ val terminalOwner = generation?.let { FabricLeaseOwner(it.surface, it.leaseHandle) }
774
+ ?: sidecarGeneration?.let { FabricLeaseOwner(it.surface, it.leaseHandle) }
775
+ terminalOwner?.let { owner ->
776
+ PreparedProseLayoutRegistry.shared.deactivateFabricLease(
777
+ owner.surface,
778
+ owner.leaseHandle
779
+ )
780
+ }
781
+ generation = null
782
+ sidecarGeneration = null
783
+ imagePipeline.cancel()
784
+ fontEnvironment.deactivate()
785
+ attachmentRevisions.reset()
786
+ stateWrapper?.destroyState()
787
+ stateWrapper = null
788
+ revisions = null
789
+ errorReporter.reset()
790
+ }
791
+
792
+ fun ownsFabricSurface(surfaceId: Int): Boolean =
793
+ generation?.surface?.surfaceId == surfaceId ||
794
+ sidecarGeneration?.surface?.surfaceId == surfaceId
795
+
796
+ private fun releaseSidecarOwnership() {
797
+ val sidecar = sidecarGeneration ?: return
798
+ // Clear only the state incarnation recorded for this view. A
799
+ // stale recycled H1 must not remove H2 merely because Fabric has
800
+ // reused the same surface/component tag.
801
+ sidecarGeneration = null
802
+ PreparedProseLayoutRegistry.shared.releaseFabricGeneration(sidecar)
803
+ }
804
+ }
805
+
806
+ companion object {
807
+ const val REACT_CLASS = "PreparedProseViewer"
808
+ private const val EVENT_ERROR = "topError"
809
+ private const val EVENT_PRESS_LINK = "topPressLink"
810
+ private const val EVENT_PRESS_MENTION = "topPressMention"
811
+ }
812
+ }
813
+
814
+ /**
815
+ * Fabric direct event carrying an already-built payload. Every viewer event
816
+ * reports a discrete outcome, so coalescing stays off: two link presses or two
817
+ * distinct resource failures on one view must both reach JS.
818
+ */
819
+ private class PreparedProseViewerEvent(
820
+ surfaceId: Int,
821
+ viewTag: Int,
822
+ private val name: String,
823
+ private val payload: WritableMap
824
+ ) : Event<PreparedProseViewerEvent>(surfaceId, viewTag) {
825
+ override fun getEventName(): String = name
826
+
827
+ override fun getEventData(): WritableMap = payload
828
+
829
+ override fun canCoalesce(): Boolean = false
830
+ }
831
+
832
+ /**
833
+ * Makes replacement accessibility notification ownership explicit. A removed
834
+ * old subtree is announced by either the final mounted artifact or, if Fabric
835
+ * cannot mount it, by the removal itself—never by both.
836
+ */
837
+ internal class FabricReplacementAccessibilityTransaction {
838
+ private enum class NotificationOwner {
839
+ NONE,
840
+ FINAL_INSTALL,
841
+ REMOVED_SUBTREE
842
+ }
843
+
844
+ private var notificationOwner = NotificationOwner.NONE
845
+
846
+ fun clearReplacing(view: PreparedProseDrawingView) {
847
+ if (view.preparedLayout == null) return
848
+ view.install(null, announceAccessibilitySubtree = false)
849
+ notificationOwner = NotificationOwner.FINAL_INSTALL
850
+ }
851
+
852
+ fun installMountedReplacement(view: PreparedProseDrawingView, ticket: PreparedMountTicket) {
853
+ installMountedReplacement(
854
+ view,
855
+ ticket.artifact,
856
+ ticket.contentOriginXPx,
857
+ ticket.contentOriginYPx
858
+ )
859
+ }
860
+
861
+ fun installMountedReplacement(view: PreparedProseDrawingView, artifact: PreparedProseLayout) {
862
+ installMountedReplacement(view, artifact, 0, 0)
863
+ }
864
+
865
+ private fun installMountedReplacement(
866
+ view: PreparedProseDrawingView,
867
+ artifact: PreparedProseLayout,
868
+ contentOriginXPx: Int,
869
+ contentOriginYPx: Int
870
+ ) {
871
+ view.install(
872
+ artifact,
873
+ announceAccessibilitySubtree = notificationOwner != NotificationOwner.REMOVED_SUBTREE,
874
+ contentOriginXPx = contentOriginXPx,
875
+ contentOriginYPx = contentOriginYPx
876
+ )
877
+ notificationOwner = NotificationOwner.NONE
878
+ }
879
+
880
+ fun finishWithoutMountedReplacement(view: PreparedProseDrawingView) {
881
+ if (notificationOwner != NotificationOwner.FINAL_INSTALL) return
882
+ view.announceAccessibilitySubtreeChanged()
883
+ notificationOwner = NotificationOwner.REMOVED_SUBTREE
884
+ }
885
+ }
886
+
887
+ /** Small lifecycle seam that guarantees Fabric emits one error per request generation. */
888
+ internal class FabricErrorReporter {
889
+ private var reportedGenerationIdentity: String? = null
890
+
891
+ fun shouldReport(generationIdentity: String): Boolean {
892
+ if (reportedGenerationIdentity == generationIdentity) return false
893
+ reportedGenerationIdentity = generationIdentity
894
+ return true
895
+ }
896
+
897
+ fun reset() {
898
+ reportedGenerationIdentity = null
899
+ }
900
+ }