@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,854 @@
1
+ package com.apollohg.editor
2
+
3
+ import android.content.Context
4
+ import android.content.res.Configuration
5
+ import android.graphics.Rect
6
+ import android.graphics.Typeface
7
+ import android.os.Bundle
8
+ import android.util.AttributeSet
9
+ import android.view.View
10
+ import android.view.ViewGroup
11
+ import android.view.ViewTreeObserver
12
+ import android.view.accessibility.AccessibilityEvent
13
+ import android.view.accessibility.AccessibilityManager
14
+ import android.view.accessibility.AccessibilityNodeInfo
15
+ import android.view.accessibility.AccessibilityNodeProvider
16
+ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
17
+ import com.apollohg.editor.viewer.PreparedProseAccessibilityNode
18
+ import com.apollohg.editor.viewer.PreparedProseDrawingView
19
+ import com.apollohg.editor.viewer.PreparedProseInstrumentation
20
+ import com.apollohg.editor.viewer.PreparedProseInteraction
21
+ import com.apollohg.editor.viewer.PreparedProseLayout
22
+ import com.apollohg.editor.viewer.PreparedProseLayoutRegistry
23
+ import com.apollohg.editor.viewer.ProseViewerRequest
24
+ import com.apollohg.editor.viewer.ViewerAttachmentRevisionState
25
+ import com.apollohg.editor.viewer.ViewerDocument
26
+ import com.apollohg.editor.viewer.ViewerFontEnvironment
27
+ import com.apollohg.editor.viewer.ViewerImageAttachment
28
+ import com.apollohg.editor.viewer.ViewerImagePipeline
29
+ import com.apollohg.editor.viewer.accessibilityNodeVisibleOnScreen
30
+ import org.json.JSONArray
31
+ import org.json.JSONObject
32
+
33
+ sealed interface ProseViewerSource {
34
+ val value: String
35
+ val kind: String
36
+
37
+ data class Json(override val value: String) : ProseViewerSource {
38
+ override val kind: String = "json"
39
+ }
40
+
41
+ data class Html(override val value: String) : ProseViewerSource {
42
+ override val kind: String = "html"
43
+ }
44
+ }
45
+
46
+ data class ProseViewerConfiguration(
47
+ val configJson: String,
48
+ val themeJson: String? = null,
49
+ val imagePolicyJson: String? = null,
50
+ val imagesEnabled: Boolean = true,
51
+ val collapsesWhenEmpty: Boolean = true
52
+ )
53
+
54
+ @JvmInline
55
+ value class ProseViewerErrorCode(val value: String) {
56
+ companion object {
57
+ val INVALID_WIDTH = ProseViewerErrorCode("INVALID_WIDTH")
58
+ val LAYOUT_FAILED = ProseViewerErrorCode("LAYOUT_FAILED")
59
+ val RESOURCE_LOAD_FAILED = ProseViewerErrorCode("RESOURCE_LOAD_FAILED")
60
+ val INVALID_MENTION_ATTRIBUTES = ProseViewerErrorCode("INVALID_MENTION_ATTRIBUTES")
61
+ }
62
+ }
63
+
64
+ data class ProseViewerError(
65
+ val domain: String,
66
+ val code: ProseViewerErrorCode,
67
+ override val message: String
68
+ ) : RuntimeException(message) {
69
+ companion object {
70
+ fun compiler(domain: String, code: String, message: String) =
71
+ ProseViewerError(domain, ProseViewerErrorCode(code), message)
72
+
73
+ fun invalidWidth() = ProseViewerError(
74
+ "viewer.host",
75
+ ProseViewerErrorCode.INVALID_WIDTH,
76
+ "A finite positive width is required for prose measurement."
77
+ )
78
+
79
+ fun layout(message: String) =
80
+ ProseViewerError("viewer.layout", ProseViewerErrorCode.LAYOUT_FAILED, message)
81
+
82
+ fun resource() = ProseViewerError(
83
+ "viewer.resource",
84
+ ProseViewerErrorCode.RESOURCE_LOAD_FAILED,
85
+ "An image resource could not be loaded."
86
+ )
87
+ }
88
+ }
89
+
90
+ /** A mention activated from an embedded prose viewer. */
91
+ data class ProseViewerMention(val docPos: Long, val label: String, val attrs: Map<String, Any?>)
92
+
93
+ /** Interaction callbacks for an embedded Android prose viewer. */
94
+ interface ProseViewerInteractionListener {
95
+ fun onLinkTap(view: ProseViewerView, href: String, text: String)
96
+ fun onMentionTap(view: ProseViewerView, mention: ProseViewerMention)
97
+ fun onViewerError(view: ProseViewerView, error: ProseViewerError) = Unit
98
+ }
99
+
100
+ abstract class ProseViewerInteractionListenerAdapter : ProseViewerInteractionListener {
101
+ override fun onLinkTap(view: ProseViewerView, href: String, text: String) = Unit
102
+ override fun onMentionTap(view: ProseViewerView, mention: ProseViewerMention) = Unit
103
+ }
104
+
105
+ /**
106
+ * Display-only prose viewer for Android View hosts.
107
+ */
108
+ class ProseViewerView @JvmOverloads constructor(
109
+ context: Context,
110
+ attrs: AttributeSet? = null,
111
+ defStyleAttr: Int = 0
112
+ ) : ViewGroup(context, attrs, defStyleAttr) {
113
+ private val accessibilityManager = context.getSystemService(AccessibilityManager::class.java)
114
+ private val preparedInstrumentationOwner = "direct-${System.identityHashCode(this)}"
115
+ internal constructor(context: Context, registry: PreparedProseLayoutRegistry) : this(context) {
116
+ layoutRegistry = registry
117
+ }
118
+
119
+ var interactionListener: ProseViewerInteractionListener? = null
120
+ set(value) {
121
+ if (field === value) return
122
+ clearVirtualAccessibilityFocus()
123
+ field = value
124
+ updatePreparedInteractionCapabilities()
125
+ notifyAccessibilitySubtreeChanged()
126
+ }
127
+
128
+ private var layoutRegistry = PreparedProseLayoutRegistry.shared
129
+ private val preparedDrawingView = PreparedProseDrawingView(context)
130
+ private var preparedRequest: ProseViewerRequest? = null
131
+ private var retainedDocument: ViewerDocument? = null
132
+ private var preparedArtifact: PreparedProseLayout? = null
133
+
134
+ // Detach drops the direct registration but deliberately retains the
135
+ // immutable artifact for exact, no-recompile reattachment.
136
+ private var directMountedArtifact: PreparedProseLayout? = null
137
+ private var directError: ProseViewerError? = null
138
+ private var reportedGenerationIdentity: String? = null
139
+ private val attachmentRevisions = ViewerAttachmentRevisionState()
140
+ private val fontEnvironment = ViewerFontEnvironment()
141
+ private val viewerImagePipeline = ViewerImagePipeline()
142
+
143
+ private var accessibilityFocusedNode: FocusedVirtualNode? = null
144
+ private var preparedAccessibilityGeneration: String? = null
145
+ private val scrollChangedListener = ViewTreeObserver.OnScrollChangedListener {
146
+ reconcileVirtualAccessibilityFocus()
147
+ }
148
+
149
+ internal var linkTapsEnabled = true
150
+ set(value) {
151
+ if (field == value) return
152
+ clearVirtualAccessibilityFocus()
153
+ field = value
154
+ updatePreparedInteractionCapabilities()
155
+ notifyAccessibilitySubtreeChanged()
156
+ }
157
+ internal var onLinkTapForTesting: (() -> Unit)? = null
158
+ set(value) {
159
+ if (field === value) return
160
+ clearVirtualAccessibilityFocus()
161
+ field = value
162
+ updatePreparedInteractionCapabilities()
163
+ notifyAccessibilitySubtreeChanged()
164
+ }
165
+ internal var onMentionTapForTesting: (() -> Unit)? = null
166
+ set(value) {
167
+ if (field === value) return
168
+ clearVirtualAccessibilityFocus()
169
+ field = value
170
+ updatePreparedInteractionCapabilities()
171
+ notifyAccessibilitySubtreeChanged()
172
+ }
173
+ internal var accessibilityVisibilityForTesting: ((Rect) -> Boolean)? = null
174
+ internal val preparedLayoutForTesting: PreparedProseLayout?
175
+ get() = preparedArtifact
176
+
177
+ /** Mounted host total; the shared layout cache excludes mutable sidecars. */
178
+ internal val preparedSurfaceRetainedBytesForTesting: Long
179
+ get() = saturatedRetainedBytes(
180
+ preparedArtifact?.retainedBytes ?: 0L,
181
+ attachmentRevisions.retainedPublicationBytesForTesting.toLong(),
182
+ preparedDrawingView.retainedImagePixelsBytesForTesting
183
+ )
184
+
185
+ init {
186
+ DecodedBitmapBudget.shared(context)
187
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
188
+
189
+ // The public facade owns virtual accessibility. The drawing child is
190
+ // still interactive, but must not expose a duplicate virtual subtree.
191
+ preparedDrawingView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
192
+ preparedDrawingView.publishesAccessibilitySubtree = false
193
+ updatePreparedInteractionCapabilities()
194
+ preparedDrawingView.onCodeHighlightsReady = {
195
+ preparedRequest?.let {
196
+ preparedRequest =
197
+ it.copy(nativeFontRevision = it.nativeFontRevision + 1)
198
+ }
199
+ requestLayout()
200
+ }
201
+ preparedDrawingView.onInteractionActivated = { activatePreparedInteraction(it) }
202
+ viewerImagePipeline.onPixels = { attachment, lease ->
203
+ val current = preparedRequest
204
+ if (current != null &&
205
+ viewerImagePipeline.acceptsCompletion(current.semanticGenerationIdentity)
206
+ ) {
207
+ preparedDrawingView.putImageLease(attachment.id, lease)
208
+ } else {
209
+ lease.close()
210
+ }
211
+ }
212
+ viewerImagePipeline.onPixelsReleased = preparedDrawingView::removeImageLeases
213
+ viewerImagePipeline.onIntrinsicMetadata = { attachment, width, height ->
214
+ applyIntrinsicImageMetadata(attachment, width, height)
215
+ }
216
+ viewerImagePipeline.onResourceFailure =
217
+ { attachment -> reportResourceFailureIfNeeded(attachment) }
218
+ fontEnvironment.onInvalidated = { revision -> applyFontEnvironmentRevision(revision) }
219
+ addView(
220
+ preparedDrawingView,
221
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
222
+ )
223
+ }
224
+
225
+ /**
226
+ * Starts an immutable direct-content generation. Compilation is retained through the first
227
+ * finite measurement even when the registry evicts its unmounted cache entry.
228
+ */
229
+ fun apply(source: ProseViewerSource, configuration: ProseViewerConfiguration): Boolean {
230
+ fontEnvironment.activate()
231
+ val currentFontRevision = fontEnvironment.revision
232
+ preparedRequest?.let { current ->
233
+ if (current.source == source && current.configuration == configuration &&
234
+ current.fontEnvironmentRevision == currentFontRevision
235
+ ) {
236
+ return directError == null
237
+ }
238
+ if (current.source == source && current.configuration == configuration) {
239
+ preparedRequest = current.copy(fontEnvironmentRevision = currentFontRevision)
240
+ requestLayout()
241
+ return directError == null
242
+ }
243
+ }
244
+ val next = ProseViewerRequest(
245
+ source,
246
+ configuration,
247
+ fontEnvironmentRevision = currentFontRevision,
248
+ attachmentRevision = 0
249
+ )
250
+ PreparedProseInstrumentation.invalidated(
251
+ PreparedProseInstrumentation.InvalidationReason.CONTENT
252
+ )
253
+ attachmentRevisions.beginSemanticGeneration(next.semanticGenerationIdentity)
254
+ clearVirtualAccessibilityFocus()
255
+ preparedRequest = next
256
+ retainedDocument = null
257
+ preparedArtifact = null
258
+ releaseDirectMountedArtifact()
259
+ viewerImagePipeline.cancel()
260
+ preparedDrawingView.clearImageLeases()
261
+ directError = null
262
+ reportedGenerationIdentity = null
263
+ preparedAccessibilityGeneration = null
264
+ // The replacement artifact owns the observable subtree transition.
265
+ preparedDrawingView.install(null, announceAccessibilitySubtree = false)
266
+ preparedDrawingView.visibility = View.VISIBLE
267
+ return try {
268
+ retainedDocument = layoutRegistry.compileDocument(next)
269
+ requestLayout()
270
+ true
271
+ } catch (error: ProseViewerError) {
272
+ directError = error
273
+ requestLayout()
274
+ false
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Clears content and pending work for a recycled host view.
280
+ *
281
+ * The interaction listener is retained so holders may assign it once.
282
+ */
283
+ fun prepareForReuse() {
284
+ PreparedProseInstrumentation.invalidated(
285
+ PreparedProseInstrumentation.InvalidationReason.REUSE
286
+ )
287
+ fontEnvironment.deactivate()
288
+ clearDirectGeneration()
289
+ clearVirtualAccessibilityFocus()
290
+ requestLayout()
291
+ notifyAccessibilitySubtreeChanged()
292
+ }
293
+
294
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
295
+ preparedRequest?.let { request ->
296
+ val widthMode = MeasureSpec.getMode(widthMeasureSpec)
297
+ val availableWidth = (
298
+ MeasureSpec.getSize(
299
+ widthMeasureSpec
300
+ ) - paddingLeft - paddingRight
301
+ )
302
+ val artifact = layoutRegistry.measure(
303
+ request = request,
304
+ widthPx = if (widthMode == MeasureSpec.UNSPECIFIED) 0 else availableWidth,
305
+ density = resources.displayMetrics.density,
306
+ compiledDocument = retainedDocument,
307
+ fontScale = resources.configuration.fontScale,
308
+ measurementImageState = attachmentRevisions
309
+ )
310
+ val artifactChanged = preparedArtifact !== artifact
311
+ val accessibilityChanged =
312
+ artifactChanged ||
313
+ preparedAccessibilityGeneration != artifact.key.generationIdentity
314
+ if (accessibilityChanged) {
315
+ clearVirtualAccessibilityFocus()
316
+ }
317
+ preparedArtifact = artifact
318
+ registerDirectMountedArtifactIfAttached(artifact)
319
+ preparedDrawingView.install(artifact)
320
+ if (accessibilityChanged) {
321
+ preparedAccessibilityGeneration = artifact.key.generationIdentity
322
+ notifyAccessibilitySubtreeChanged()
323
+ }
324
+ reportDirectErrorIfNeeded(request, artifact.error ?: directError)
325
+ val desiredWidth = artifact.widthPx + paddingLeft + paddingRight
326
+ val intrinsicHeight = artifact.heightPx + paddingTop + paddingBottom
327
+ val measuredWidth = resolveSize(desiredWidth, widthMeasureSpec)
328
+ val measuredHeight = when (MeasureSpec.getMode(heightMeasureSpec)) {
329
+ MeasureSpec.EXACTLY -> MeasureSpec.getSize(heightMeasureSpec)
330
+
331
+ MeasureSpec.AT_MOST -> intrinsicHeight.coerceAtMost(
332
+ MeasureSpec.getSize(heightMeasureSpec)
333
+ )
334
+
335
+ else -> intrinsicHeight
336
+ }
337
+ setMeasuredDimension(measuredWidth, measuredHeight)
338
+ return
339
+ }
340
+ setMeasuredDimension(resolveSize(0, widthMeasureSpec), 0)
341
+ }
342
+
343
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
344
+ if (preparedRequest != null) {
345
+ preparedDrawingView.layout(
346
+ paddingLeft,
347
+ paddingTop,
348
+ (right - left - paddingRight).coerceAtLeast(paddingLeft),
349
+ (bottom - top - paddingBottom).coerceAtLeast(paddingTop)
350
+ )
351
+ reconcileVirtualAccessibilityFocus()
352
+ requestVisibleImageAttachments()
353
+ return
354
+ }
355
+ }
356
+
357
+ override fun dispatchDraw(canvas: android.graphics.Canvas) {
358
+ reconcileVirtualAccessibilityFocus()
359
+ super.dispatchDraw(canvas)
360
+ }
361
+
362
+ override fun onDetachedFromWindow() {
363
+ if (viewTreeObserver.isAlive) {
364
+ viewTreeObserver.removeOnScrollChangedListener(scrollChangedListener)
365
+ }
366
+ preparePreparedHostForWindowDetachment()
367
+ fontEnvironment.deactivate()
368
+ super.onDetachedFromWindow()
369
+ }
370
+
371
+ override fun onAttachedToWindow() {
372
+ super.onAttachedToWindow()
373
+ viewTreeObserver.addOnScrollChangedListener(scrollChangedListener)
374
+ if (preparedRequest != null) fontEnvironment.activate(deliverPending = true)
375
+ // Detachment only cancels image work. The immutable prepared artifact
376
+ // remains installed, so a direct host can draw/measure immediately on
377
+ // reattach without a semantic replacement or republish.
378
+ if (preparedRequest != null) {
379
+ preparedArtifact?.let(::registerDirectMountedArtifactIfAttached)
380
+ requestLayout()
381
+ preparedDrawingView.invalidate()
382
+ }
383
+ requestVisibleImageAttachments()
384
+ reconcileVirtualAccessibilityFocus()
385
+ }
386
+
387
+ override fun onVisibilityChanged(changedView: View, visibility: Int) {
388
+ super.onVisibilityChanged(changedView, visibility)
389
+ reconcileVirtualAccessibilityFocus()
390
+ }
391
+
392
+ override fun onWindowVisibilityChanged(visibility: Int) {
393
+ super.onWindowVisibilityChanged(visibility)
394
+ reconcileVirtualAccessibilityFocus()
395
+ }
396
+
397
+ override fun onConfigurationChanged(newConfig: Configuration) {
398
+ super.onConfigurationChanged(newConfig)
399
+ fontEnvironment.onConfigurationChanged(newConfig)
400
+ }
401
+
402
+ /** Explicit hook for React Native font loaders. */
403
+ fun invalidateFontEnvironment() = fontEnvironment.invalidateRegisteredFonts()
404
+
405
+ /**
406
+ * Removes a directly owned prepared subtree. Virtual focus must clear
407
+ * before the artifact disappears and before its subtree notification.
408
+ */
409
+ internal fun preparePreparedHostForWindowDetachment() {
410
+ clearVirtualAccessibilityFocus()
411
+ releaseDirectMountedArtifact()
412
+ if (preparedRequest != null) {
413
+ viewerImagePipeline.cancel()
414
+ // Cancellation/detachment does not create a new semantic source;
415
+ // retain its artifact/publication bits and revision for a later
416
+ // remount. Pixel ownership is released with the drawing map.
417
+ preparedDrawingView.clearImageLeases()
418
+ }
419
+ }
420
+
421
+ private fun reportDirectErrorIfNeeded(request: ProseViewerRequest, error: ProseViewerError?) {
422
+ if (error == null ||
423
+ reportedGenerationIdentity == request.semanticGenerationIdentity
424
+ ) {
425
+ return
426
+ }
427
+ reportedGenerationIdentity = request.semanticGenerationIdentity
428
+ interactionListener?.onViewerError(this, error)
429
+ }
430
+
431
+ private fun reportResourceFailureIfNeeded(attachment: ViewerImageAttachment) {
432
+ if (!attachmentRevisions.recordResourceFailure(attachment.ordinal)) return
433
+ interactionListener?.onViewerError(this, ProseViewerError.resource())
434
+ }
435
+
436
+ private fun clearDirectGeneration() {
437
+ clearVirtualAccessibilityFocus()
438
+ preparedRequest = null
439
+ retainedDocument = null
440
+ preparedArtifact = null
441
+ releaseDirectMountedArtifact()
442
+ viewerImagePipeline.cancel()
443
+ attachmentRevisions.reset()
444
+ preparedDrawingView.clearImageLeases()
445
+ directError = null
446
+ reportedGenerationIdentity = null
447
+ preparedDrawingView.install(null, announceAccessibilitySubtree = false)
448
+ preparedDrawingView.visibility = View.GONE
449
+ preparedAccessibilityGeneration = null
450
+ }
451
+
452
+ /**
453
+ * Android can measure a View that never enters a window. Keep that
454
+ * artifact on this View, but do not globally pin it as a direct mount
455
+ * until attachment gives us a deterministic matching release callback.
456
+ */
457
+ private fun registerDirectMountedArtifactIfAttached(artifact: PreparedProseLayout) {
458
+ if (!isAttachedToWindow) return
459
+ if (directMountedArtifact === artifact) return
460
+ releaseDirectMountedArtifact()
461
+ layoutRegistry.registerDirectMounted(preparedInstrumentationOwner, artifact)
462
+ directMountedArtifact = artifact
463
+ }
464
+
465
+ private fun releaseDirectMountedArtifact() {
466
+ if (directMountedArtifact == null) return
467
+ layoutRegistry.releaseDirectMounted(preparedInstrumentationOwner)
468
+ directMountedArtifact = null
469
+ }
470
+
471
+ private fun configureImageGeneration(artifact: PreparedProseLayout) {
472
+ val request = preparedRequest ?: return
473
+ attachmentRevisions.beginSemanticGeneration(request.semanticGenerationIdentity)
474
+ attachmentRevisions.admit(artifact.imageAttachments.size)
475
+ viewerImagePipeline.begin(
476
+ request.semanticGenerationIdentity,
477
+ request.configuration.imagesEnabled,
478
+ ImageLoadingPolicy.fromJson(request.configuration.imagePolicyJson)
479
+ )
480
+ }
481
+
482
+ private fun requestVisibleImageAttachments() {
483
+ val artifact = preparedArtifact ?: return
484
+ if (!isAttachedToWindow || preparedDrawingView.visibility != View.VISIBLE ||
485
+ !preparedDrawingView.isShown
486
+ ) {
487
+ return
488
+ }
489
+ val visible = Rect()
490
+ if (!preparedDrawingView.getGlobalVisibleRect(visible) || visible.isEmpty) return
491
+ val location = IntArray(2)
492
+ preparedDrawingView.getLocationOnScreen(location)
493
+ visible.offset(-location[0], -location[1])
494
+ if (!visible.intersect(Rect(0, 0, preparedDrawingView.width, preparedDrawingView.height)) ||
495
+ visible.isEmpty
496
+ ) {
497
+ return
498
+ }
499
+ configureImageGeneration(artifact)
500
+ viewerImagePipeline.updateVisibleRect(
501
+ visible,
502
+ artifact.imageAttachments
503
+ )
504
+ }
505
+
506
+ private fun applyIntrinsicImageMetadata(
507
+ attachment: ViewerImageAttachment,
508
+ width: Int,
509
+ height: Int
510
+ ) {
511
+ val request = preparedRequest ?: return
512
+ if (!viewerImagePipeline.acceptsCompletion(request.semanticGenerationIdentity)) return
513
+ if (!attachmentRevisions.recordIntrinsicSize(
514
+ attachment.id,
515
+ attachment.ordinal,
516
+ width,
517
+ height,
518
+ attachment.declaredSize
519
+ )
520
+ ) {
521
+ return
522
+ }
523
+ preparedRequest = request.copy(attachmentRevision = attachmentRevisions.revision)
524
+ PreparedProseInstrumentation.invalidated(
525
+ PreparedProseInstrumentation.InvalidationReason.ATTACHMENT
526
+ )
527
+ PreparedProseInstrumentation.retained(
528
+ PreparedProseInstrumentation.Owner.SIDECARS,
529
+ "direct-${System.identityHashCode(this)}",
530
+ attachmentRevisions.retainedPublicationBytesForTesting.toLong()
531
+ )
532
+ requestLayout()
533
+ }
534
+
535
+ private fun applyFontEnvironmentRevision(revision: Long) {
536
+ val request = preparedRequest ?: return
537
+ if (revision <= request.fontEnvironmentRevision) return
538
+ preparedRequest = request.copy(fontEnvironmentRevision = revision)
539
+ PreparedProseInstrumentation.invalidated(
540
+ PreparedProseInstrumentation.InvalidationReason.FONT
541
+ )
542
+ requestLayout()
543
+ }
544
+
545
+ private fun saturatedRetainedBytes(vararg values: Long): Long = values.fold(0L) {
546
+ total,
547
+ value
548
+ ->
549
+ if (value > 0 && total > Long.MAX_VALUE - value) Long.MAX_VALUE else total + value
550
+ }
551
+
552
+ private fun activatePreparedInteraction(interaction: PreparedProseInteraction): Boolean {
553
+ return when (interaction.kind) {
554
+ PreparedProseInteraction.Kind.LINK -> {
555
+ val href = interaction.href ?: return false
556
+ if (!linkInteractionsActionable()) return false
557
+ onLinkTapForTesting?.invoke()
558
+ ?: interactionListener?.onLinkTap(this, href, interaction.visibleText)
559
+ true
560
+ }
561
+
562
+ PreparedProseInteraction.Kind.MENTION -> {
563
+ if (!mentionInteractionsActionable()) return false
564
+ val docPos = interaction.docPos ?: return false
565
+ val attrs = interaction.attrsJson?.let(::parseMentionAttrs)
566
+ if (attrs == null) {
567
+ interactionListener?.onViewerError(
568
+ this,
569
+ ProseViewerError.compiler(
570
+ "viewer",
571
+ ProseViewerErrorCode.INVALID_MENTION_ATTRIBUTES.value,
572
+ "The prepared mention attributes are not a JSON object."
573
+ )
574
+ )
575
+ return false
576
+ }
577
+ onMentionTapForTesting?.invoke()
578
+ ?: interactionListener?.onMentionTap(
579
+ this,
580
+ ProseViewerMention(docPos, interaction.label, attrs)
581
+ )
582
+ true
583
+ }
584
+ }
585
+ }
586
+
587
+ private fun parseMentionAttrs(json: String): Map<String, Any?>? = runCatching {
588
+ jsonObjectToMap(JSONObject(json))
589
+ }.getOrNull()
590
+
591
+ private fun jsonObjectToMap(value: JSONObject): Map<String, Any?> = buildMap {
592
+ value.keys().forEach { key -> put(key, jsonValue(value.get(key))) }
593
+ }
594
+
595
+ private fun jsonArrayToList(value: JSONArray): List<Any?> =
596
+ List(value.length()) { index -> jsonValue(value.get(index)) }
597
+
598
+ private fun jsonValue(value: Any): Any? = when (value) {
599
+ JSONObject.NULL -> null
600
+ is JSONObject -> jsonObjectToMap(value)
601
+ is JSONArray -> jsonArrayToList(value)
602
+ else -> value
603
+ }
604
+
605
+ internal fun activatePreparedInteractionForTesting(
606
+ interaction: PreparedProseInteraction
607
+ ): Boolean = activatePreparedInteraction(interaction)
608
+
609
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
610
+ super.onInitializeAccessibilityNodeInfo(info)
611
+ info.className = android.widget.TextView::class.java.name
612
+ info.text = preparedAccessibleNodes().joinToString(" ") { it.label }
613
+ repeat(preparedAccessibleNodes().size) { index ->
614
+ info.addChild(this, index + FIRST_VIRTUAL_ANNOTATION_ID)
615
+ }
616
+ }
617
+
618
+ override fun getAccessibilityNodeProvider(): AccessibilityNodeProvider = annotationNodeProvider
619
+
620
+ // The replacement constructors are API 30 and this module's minSdk is 24;
621
+ // on API 30+ obtain() only delegates to them.
622
+ @Suppress("DEPRECATION")
623
+ private val annotationNodeProvider = object : AccessibilityNodeProvider() {
624
+ override fun createAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfo? {
625
+ if (virtualViewId == View.NO_ID) {
626
+ return AccessibilityNodeInfo.obtain(this@ProseViewerView).also {
627
+ onInitializeAccessibilityNodeInfo(it)
628
+ }
629
+ }
630
+ return preparedAccessibilityNodeInfo(virtualViewId)
631
+ }
632
+
633
+ override fun performAction(virtualViewId: Int, action: Int, arguments: Bundle?): Boolean =
634
+ performPreparedAccessibilityAction(virtualViewId, action)
635
+ }
636
+
637
+ private fun preparedAccessibleNodes() = preparedArtifact?.accessibilityNodes.orEmpty().filter {
638
+ when (it.role) {
639
+ PreparedProseAccessibilityNode.Role.LINK -> linkInteractionsActionable()
640
+ PreparedProseAccessibilityNode.Role.MENTION -> mentionInteractionsActionable()
641
+ }
642
+ }
643
+
644
+ // AccessibilityNodeInfo() is API 30; setBoundsInParent has no replacement
645
+ // and API 24-28 services still read it.
646
+ @Suppress("DEPRECATION")
647
+ private fun preparedAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfo? {
648
+ val node =
649
+ preparedAccessibleNodes().getOrNull(virtualViewId - FIRST_VIRTUAL_ANNOTATION_ID)
650
+ ?: return null
651
+ val parentBounds = Rect(node.bounds).apply {
652
+ offset(preparedDrawingView.left, preparedDrawingView.top)
653
+ }
654
+ val screenBounds = preparedAccessibilityScreenBounds(node)
655
+ val visibleToUser = accessibilityNodeVisibleOnScreen(screenBounds)
656
+ reconcileVirtualAccessibilityFocus()
657
+ val identity = accessibilityIdentity(node)
658
+ return AccessibilityNodeInfo.obtain().apply {
659
+ packageName = context.packageName
660
+ className = android.widget.Button::class.java.name
661
+ setSource(this@ProseViewerView, virtualViewId)
662
+ setParent(this@ProseViewerView)
663
+ text = node.label
664
+ contentDescription = node.label
665
+ isClickable = true
666
+ isFocusable = true
667
+ AndroidApiCompat.setScreenReaderFocusable(this, true)
668
+ isAccessibilityFocused = accessibilityFocusedNode?.identity == identity
669
+ isVisibleToUser = visibleToUser
670
+ setBoundsInParent(parentBounds)
671
+ setBoundsInScreen(screenBounds)
672
+ addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)
673
+ addAction(
674
+ if (isAccessibilityFocused) {
675
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS
676
+ } else {
677
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS
678
+ }
679
+ )
680
+ AccessibilityNodeInfoCompat.wrap(this).roleDescription =
681
+ if (node.role ==
682
+ com.apollohg.editor.viewer.PreparedProseAccessibilityNode.Role.LINK
683
+ ) {
684
+ "link"
685
+ } else {
686
+ "mention"
687
+ }
688
+ }
689
+ }
690
+
691
+ private fun performPreparedAccessibilityAction(virtualViewId: Int, action: Int): Boolean {
692
+ val node =
693
+ preparedAccessibleNodes().getOrNull(virtualViewId - FIRST_VIRTUAL_ANNOTATION_ID)
694
+ ?: return false
695
+ return when (action) {
696
+ AccessibilityNodeInfo.ACTION_CLICK -> if (preparedAccessibilityNodeVisible(node)) {
697
+ preparedArtifact?.interactions?.getOrNull(node.interactionIndex)
698
+ ?.let(::activatePreparedInteraction) ?: false
699
+ } else {
700
+ false
701
+ }
702
+
703
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS ->
704
+ requestVirtualAccessibilityFocus(
705
+ virtualViewId
706
+ )
707
+
708
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS ->
709
+ clearVirtualAccessibilityFocus(
710
+ virtualViewId
711
+ )
712
+
713
+ else -> false
714
+ }
715
+ }
716
+
717
+ private fun requestVirtualAccessibilityFocus(virtualViewId: Int): Boolean {
718
+ val node = preparedAccessibleNodes()
719
+ .getOrNull(virtualViewId - FIRST_VIRTUAL_ANNOTATION_ID) ?: return false
720
+ if (!preparedAccessibilityNodeVisible(node)) return false
721
+ val identity = accessibilityIdentity(node)
722
+ if (accessibilityFocusedNode?.identity == identity) return false
723
+ accessibilityFocusedNode?.let { previous ->
724
+ accessibilityFocusedNode = null
725
+ sendVirtualAccessibilityEvent(
726
+ previous.virtualId,
727
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
728
+ )
729
+ }
730
+ accessibilityFocusedNode = FocusedVirtualNode(virtualViewId, identity)
731
+ invalidate()
732
+ sendVirtualAccessibilityEvent(
733
+ virtualViewId,
734
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
735
+ )
736
+ return true
737
+ }
738
+
739
+ private fun clearVirtualAccessibilityFocus(
740
+ virtualViewId: Int = accessibilityFocusedNode?.virtualId ?: View.NO_ID
741
+ ): Boolean {
742
+ val focused = accessibilityFocusedNode ?: return false
743
+ if (virtualViewId == View.NO_ID || virtualViewId != focused.virtualId) return false
744
+ accessibilityFocusedNode = null
745
+ invalidate()
746
+ sendVirtualAccessibilityEvent(
747
+ virtualViewId,
748
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
749
+ )
750
+ return true
751
+ }
752
+
753
+ private fun reconcileVirtualAccessibilityFocus() {
754
+ val focused = accessibilityFocusedNode ?: return
755
+ val nodes = preparedAccessibleNodes()
756
+ val index = nodes.indexOfFirst { accessibilityIdentity(it) == focused.identity }
757
+ if (index < 0 || index + FIRST_VIRTUAL_ANNOTATION_ID != focused.virtualId) {
758
+ clearVirtualAccessibilityFocus(focused.virtualId)
759
+ return
760
+ }
761
+ if (!preparedAccessibilityNodeVisible(nodes[index])) {
762
+ clearVirtualAccessibilityFocus(focused.virtualId)
763
+ }
764
+ }
765
+
766
+ private fun preparedAccessibilityNodeVisible(node: PreparedProseAccessibilityNode): Boolean =
767
+ preparedAccessibilityScreenBounds(node).let { bounds ->
768
+ accessibilityVisibilityForTesting?.invoke(bounds)
769
+ ?: accessibilityNodeVisibleOnScreen(bounds)
770
+ }
771
+
772
+ private fun preparedAccessibilityScreenBounds(node: PreparedProseAccessibilityNode): Rect {
773
+ val bounds = Rect(node.bounds).apply {
774
+ offset(preparedDrawingView.left, preparedDrawingView.top)
775
+ }
776
+ val location = IntArray(2)
777
+ getLocationOnScreen(location)
778
+ bounds.offset(location[0], location[1])
779
+ return bounds
780
+ }
781
+
782
+ private fun accessibilityIdentity(node: PreparedProseAccessibilityNode) =
783
+ AccessibilityNodeIdentity(
784
+ preparedArtifact?.key?.generationIdentity,
785
+ node.interactionIndex,
786
+ node.role,
787
+ node.label
788
+ )
789
+
790
+ private fun updatePreparedInteractionCapabilities() {
791
+ preparedDrawingView.linkInteractionsEnabled = linkInteractionsActionable()
792
+ preparedDrawingView.mentionInteractionsEnabled = mentionInteractionsActionable()
793
+ }
794
+
795
+ private fun linkInteractionsActionable(): Boolean =
796
+ linkTapsEnabled && (onLinkTapForTesting != null || interactionListener != null)
797
+
798
+ private fun mentionInteractionsActionable(): Boolean =
799
+ onMentionTapForTesting != null || interactionListener != null
800
+
801
+ // AccessibilityEvent(Int) is API 30; see the node provider above.
802
+ @Suppress("DEPRECATION")
803
+ private fun sendVirtualAccessibilityEvent(virtualViewId: Int, eventType: Int) {
804
+ if (!accessibilityManager.isEnabled) return
805
+ val event = AccessibilityEvent.obtain(eventType).apply {
806
+ packageName = context.packageName
807
+ className = android.widget.Button::class.java.name
808
+ setSource(this@ProseViewerView, virtualViewId)
809
+ }
810
+ parent?.requestSendAccessibilityEvent(this, event)
811
+ }
812
+
813
+ @Suppress("DEPRECATION")
814
+ private fun notifyAccessibilitySubtreeChanged() {
815
+ if (!accessibilityManager.isEnabled) return
816
+ val event = AccessibilityEvent.obtain(
817
+ AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
818
+ ).apply {
819
+ packageName = context.packageName
820
+ className = android.widget.TextView::class.java.name
821
+ contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
822
+ setSource(this@ProseViewerView)
823
+ }
824
+ parent?.requestSendAccessibilityEvent(this, event)
825
+ }
826
+
827
+ companion object {
828
+ private const val FIRST_VIRTUAL_ANNOTATION_ID = 1
829
+
830
+ /**
831
+ * Explicit availability signal for custom-family loaders. Unknown
832
+ * Typeface fallback is intentionally not treated as a missing font.
833
+ */
834
+ @JvmStatic
835
+ fun registerAvailableFontFamily(family: String, typeface: Typeface) =
836
+ ViewerFontEnvironment.registerAvailableFamily(family, typeface)
837
+
838
+ @JvmStatic
839
+ fun markFontFamilyUnavailable(family: String) =
840
+ ViewerFontEnvironment.markFamilyUnavailable(family)
841
+ }
842
+
843
+ private data class AccessibilityNodeIdentity(
844
+ val generation: String?,
845
+ val interactionIndex: Int,
846
+ val role: PreparedProseAccessibilityNode.Role,
847
+ val label: String
848
+ )
849
+
850
+ private data class FocusedVirtualNode(
851
+ val virtualId: Int,
852
+ val identity: AccessibilityNodeIdentity
853
+ )
854
+ }