@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,860 @@
1
+ package com.apollohg.editor
2
+
3
+ import android.os.SystemClock
4
+ import java.net.URI
5
+ import java.util.ArrayDeque
6
+ import java.util.concurrent.CompletableFuture
7
+ import java.util.concurrent.Future
8
+ import java.util.concurrent.RejectedExecutionException
9
+ import java.util.concurrent.ScheduledFuture
10
+ import java.util.concurrent.ScheduledThreadPoolExecutor
11
+ import java.util.concurrent.TimeUnit
12
+ import java.util.concurrent.atomic.AtomicBoolean
13
+ import java.util.concurrent.atomic.AtomicReference
14
+ import okhttp3.Request
15
+ import okio.ByteString.Companion.toByteString
16
+ import org.json.JSONArray
17
+ import org.json.JSONObject
18
+
19
+ internal data class NativeCollaborationTransportConfig(
20
+ val url: String,
21
+ val connect: Boolean,
22
+ val protocolAdapter: NativeCollaborationProtocolAdapterConfig?,
23
+ val diagnosticEndpoint: String
24
+ ) {
25
+ companion object {
26
+ const val MAXIMUM_URL_BYTES = 4_096
27
+
28
+ fun parse(
29
+ url: String,
30
+ connect: Boolean,
31
+ protocolAdapter: NativeCollaborationProtocolAdapterConfig?
32
+ ): NativeCollaborationTransportConfig? {
33
+ if (url.toByteArray(Charsets.UTF_8).size !in 1..MAXIMUM_URL_BYTES) return null
34
+ val uri = runCatching { URI(url) }.getOrNull() ?: return null
35
+ if (uri.scheme?.lowercase() !in setOf("ws", "wss")) return null
36
+ if (uri.host.isNullOrEmpty() || uri.rawUserInfo != null ||
37
+ uri.rawFragment != null
38
+ ) {
39
+ return null
40
+ }
41
+ if (runCatching { Request.Builder().url(url).build() }.isFailure) return null
42
+ val endpoint = runCatching {
43
+ URI(uri.scheme.lowercase(), null, uri.host, uri.port, uri.rawPath, null, null)
44
+ .toASCIIString()
45
+ }.getOrNull() ?: return null
46
+ return NativeCollaborationTransportConfig(url, connect, protocolAdapter, endpoint)
47
+ }
48
+ }
49
+ }
50
+
51
+ internal data class NativeCollaborationProtocolAdapterConfig(
52
+ val protocols: List<String>,
53
+ val timeoutMillis: Long,
54
+ val terminalCloseCodes: Set<Int>
55
+ ) {
56
+ companion object {
57
+ const val MAXIMUM_FRAME_BYTES = 64 * 1_024
58
+ const val MAXIMUM_PRE_READY_FRAMES = 16
59
+ }
60
+ }
61
+
62
+ internal sealed interface NativeCollaborationProtocolFrame {
63
+ data class Text(val data: String) : NativeCollaborationProtocolFrame
64
+ data class Binary(val data: ByteArray) : NativeCollaborationProtocolFrame
65
+ }
66
+
67
+ internal enum class NativeCollaborationProtocolAdapterAction {
68
+ CONTINUE,
69
+ READY,
70
+ REJECT
71
+ }
72
+
73
+ internal data class NativeCollaborationProtocolAdapterResponse(
74
+ val action: NativeCollaborationProtocolAdapterAction,
75
+ val frames: List<NativeCollaborationProtocolFrame>
76
+ )
77
+
78
+ internal sealed interface NativeCollaborationProtocolAdapterPhase {
79
+ object Open : NativeCollaborationProtocolAdapterPhase
80
+ data class Message(val frame: NativeCollaborationProtocolFrame) :
81
+ NativeCollaborationProtocolAdapterPhase
82
+ }
83
+
84
+ internal data class NativeCollaborationProtocolAdapterEvent(
85
+ val attemptId: String,
86
+ val eventId: String,
87
+ val generation: String,
88
+ val negotiatedProtocol: String?,
89
+ val phase: NativeCollaborationProtocolAdapterPhase
90
+ )
91
+
92
+ internal enum class CollaborationWakeReason(val wireValue: String) {
93
+ LOCAL_MUTATION("localMutation"),
94
+ MODULE_MUTATION("moduleMutation"),
95
+ RECEIVE("receive"),
96
+ TIMER("timer"),
97
+ OPEN("open"),
98
+ REATTACH("reattach"),
99
+ AWARENESS("awareness")
100
+ }
101
+
102
+ internal data class AndroidCollaborationDirective(
103
+ val transportState: String,
104
+ val generationToOpen: String?,
105
+ val nextDeadlineMillis: String?,
106
+ val remoteCommitApplied: Boolean,
107
+ val peersChanged: Boolean,
108
+ val renewedLocal: Boolean,
109
+ val expiredPeers: List<String>
110
+ )
111
+
112
+ internal sealed interface AndroidCollaborationTransportEvent {
113
+ data class Directive(
114
+ val directive: AndroidCollaborationDirective,
115
+ val generation: String?,
116
+ val wakeReason: CollaborationWakeReason
117
+ ) : AndroidCollaborationTransportEvent
118
+
119
+ data class Error(val error: EditorV2Error, val generation: String?) :
120
+ AndroidCollaborationTransportEvent
121
+
122
+ data class ProtocolAdapter(val event: NativeCollaborationProtocolAdapterEvent) :
123
+ AndroidCollaborationTransportEvent
124
+ }
125
+
126
+ internal fun interface CollaborationMonotonicClock {
127
+ fun nowMillis(): Long
128
+ }
129
+
130
+ internal class AndroidCollaborationTransport(
131
+ private val editorId: String,
132
+ private val backend: EditorV2Backend = UniffiEditorV2Backend,
133
+ private val socketFactory: CollaborationSocketFactory = OkHttpCollaborationSocketFactory(),
134
+ private val clock: CollaborationMonotonicClock =
135
+ CollaborationMonotonicClock(SystemClock::elapsedRealtime),
136
+ private val eventSink: (AndroidCollaborationTransportEvent) -> Unit = {}
137
+ ) {
138
+ internal enum class HostState { FOREGROUND, BACKGROUND, DETACHED }
139
+
140
+ private val workerThread = AtomicReference<Thread?>()
141
+ private val submissionLock = Any()
142
+ private val desiredHostState = AtomicReference(HostState.FOREGROUND)
143
+ private val terminal = AtomicBoolean(false)
144
+ private val destroyedFuture = CompletableFuture<Unit>()
145
+ private val executor = ScheduledThreadPoolExecutor(1) { runnable ->
146
+ Thread(runnable, "native-editor-collaboration-$editorId").also {
147
+ it.isDaemon = true
148
+ workerThread.set(it)
149
+ }
150
+ }.apply {
151
+ removeOnCancelPolicy = true
152
+ }
153
+
154
+ private var config: NativeCollaborationTransportConfig? = null
155
+ private var socket: CollaborationSocket? = null
156
+ private var generation: String? = null
157
+ private var socketToken = 0L
158
+ private var deadline: ScheduledFuture<*>? = null
159
+ private var protocolAdapterDeadline: ScheduledFuture<*>? = null
160
+ private var inFlightLease: EditorV2OutboundLease? = null
161
+ private var networkSocketOpened = false
162
+ private var socketOpened = false
163
+ private var protocolAttemptId: String? = null
164
+ private var protocolEventSequence = 0uL
165
+ private var pendingProtocolEventId: String? = null
166
+ private var negotiatedProtocol: String? = null
167
+ private val bufferedProtocolFrames = ArrayDeque<NativeCollaborationProtocolFrame>()
168
+ private var closeReported = false
169
+ private var hostState = HostState.FOREGROUND
170
+ private var nativeAttached = true
171
+ private var destroyed = false
172
+
173
+ fun configure(newConfig: NativeCollaborationTransportConfig?): EditorV2Error? {
174
+ if (terminal.get()) return lifecycleError("collaboration transport is destroyed")
175
+ return onWorker({ lifecycleError("collaboration transport is destroyed") }) {
176
+ if (destroyed || terminal.get()) {
177
+ return@onWorker lifecycleError("collaboration transport is destroyed")
178
+ }
179
+ if (config == newConfig) {
180
+ val alreadyDrivable = canDrive()
181
+ val error = reconcileDesiredState()
182
+ if (error == null && alreadyDrivable && canDrive()) {
183
+ drive(CollaborationWakeReason.REATTACH)
184
+ }
185
+ return@onWorker error
186
+ }
187
+ hostState = desiredHostState.get()
188
+ retireNativeResources()
189
+ detachNative()?.let {
190
+ emit(it)
191
+ return@onWorker it
192
+ }
193
+ if (newConfig?.connect == true && hostState == HostState.FOREGROUND) {
194
+ reattachNative()?.let {
195
+ emit(it)
196
+ return@onWorker it
197
+ }
198
+ }
199
+ config = newConfig
200
+ if (newConfig?.connect == true && hostState == HostState.FOREGROUND) {
201
+ drive(CollaborationWakeReason.REATTACH)
202
+ }
203
+ null
204
+ }
205
+ }
206
+
207
+ fun requestHostState(state: HostState) {
208
+ if (terminal.get()) return
209
+ desiredHostState.set(state)
210
+ enqueue {
211
+ reconcileDesiredState()?.let(::emit)
212
+ }
213
+ }
214
+
215
+ fun notifyOutboundAvailable(reason: CollaborationWakeReason) {
216
+ if (terminal.get()) return
217
+ enqueue {
218
+ if (canDrive()) drive(reason)
219
+ }
220
+ }
221
+
222
+ fun resolveProtocolAdapter(
223
+ attemptId: String,
224
+ eventId: String,
225
+ response: NativeCollaborationProtocolAdapterResponse
226
+ ): EditorV2Error? {
227
+ if (terminal.get()) return lifecycleError("collaboration transport is destroyed")
228
+ return onWorker({ lifecycleError("collaboration transport is destroyed") }) {
229
+ if (destroyed || terminal.get()) {
230
+ return@onWorker lifecycleError("collaboration transport is destroyed")
231
+ }
232
+ val activeGeneration = generation
233
+ val activeSocket = socket
234
+ if (
235
+ protocolAttemptId != attemptId ||
236
+ pendingProtocolEventId != eventId ||
237
+ activeGeneration == null ||
238
+ activeSocket == null
239
+ ) {
240
+ return@onWorker null
241
+ }
242
+ pendingProtocolEventId = null
243
+ if (!sendProtocolAdapterFrames(activeSocket, response.frames)) {
244
+ failCurrentSocket(socketToken, activeGeneration, null)
245
+ return@onWorker null
246
+ }
247
+ when (response.action) {
248
+ NativeCollaborationProtocolAdapterAction.CONTINUE ->
249
+ emitNextBufferedProtocolFrame(socketToken, activeGeneration)
250
+
251
+ NativeCollaborationProtocolAdapterAction.READY -> {
252
+ activateYjs(socketToken, activeGeneration)
253
+ drainBufferedFramesAfterReady(socketToken, activeGeneration)
254
+ }
255
+
256
+ NativeCollaborationProtocolAdapterAction.REJECT ->
257
+ failCurrentSocket(socketToken, activeGeneration, 1008)
258
+ }
259
+ null
260
+ }
261
+ }
262
+
263
+ fun enterBackground() = requestHostState(HostState.BACKGROUND)
264
+
265
+ fun enterForeground() = requestHostState(HostState.FOREGROUND)
266
+
267
+ fun destroyAsync(): CompletableFuture<Unit> {
268
+ synchronized(submissionLock) {
269
+ if (!terminal.compareAndSet(false, true)) return destroyedFuture
270
+ desiredHostState.set(HostState.DETACHED)
271
+ try {
272
+ executor.execute {
273
+ try {
274
+ destroyed = true
275
+ hostState = HostState.DETACHED
276
+ retireNativeResources()
277
+ detachNative()?.let(::emit)
278
+ config = null
279
+ } finally {
280
+ executor.shutdown()
281
+ destroyedFuture.complete(Unit)
282
+ }
283
+ }
284
+ } catch (_: RejectedExecutionException) {
285
+ executor.shutdownNow()
286
+ destroyedFuture.complete(Unit)
287
+ }
288
+ }
289
+ return destroyedFuture
290
+ }
291
+
292
+ fun destroy() {
293
+ val completion = destroyAsync()
294
+ if (Thread.currentThread() === workerThread.get()) return
295
+ completion.get()
296
+ }
297
+
298
+ private fun canDrive(): Boolean =
299
+ !destroyed && !terminal.get() && hostState == HostState.FOREGROUND &&
300
+ nativeAttached && config?.connect == true
301
+
302
+ private fun reconcileDesiredState(): EditorV2Error? {
303
+ if (destroyed || terminal.get()) return null
304
+ val desired = desiredHostState.get()
305
+ val previous = hostState
306
+ hostState = desired
307
+ if (desired != HostState.FOREGROUND) {
308
+ retireNativeResources()
309
+ return detachNative()
310
+ }
311
+ if (config?.connect != true) return null
312
+ if (!nativeAttached) {
313
+ reattachNative()?.let { return it }
314
+ drive(CollaborationWakeReason.REATTACH)
315
+ } else if (previous != HostState.FOREGROUND) {
316
+ drive(CollaborationWakeReason.REATTACH)
317
+ }
318
+ return null
319
+ }
320
+
321
+ private fun detachNative(): EditorV2Error? {
322
+ if (!nativeAttached) return null
323
+ val error = backend.collaborationDetach(editorId)
324
+ if (error == null) nativeAttached = false
325
+ return error
326
+ }
327
+
328
+ private fun reattachNative(): EditorV2Error? {
329
+ if (nativeAttached) return null
330
+ val error = backend.collaborationReattach(editorId)
331
+ if (error == null) nativeAttached = true
332
+ return error
333
+ }
334
+
335
+ private fun drive(reason: CollaborationWakeReason) {
336
+ if (!canDrive()) return
337
+ consumeDirective(
338
+ backend.collaborationDrive(editorId, clock.nowMillis().toString()),
339
+ generation,
340
+ reason
341
+ )
342
+ }
343
+
344
+ private fun consumeDirective(
345
+ result: EditorV2CallResult<String>,
346
+ eventGeneration: String?,
347
+ reason: CollaborationWakeReason
348
+ ): Boolean = when (result) {
349
+ is EditorV2CallResult.Err -> {
350
+ emit(result.error, eventGeneration)
351
+ false
352
+ }
353
+
354
+ is EditorV2CallResult.Ok -> {
355
+ val directive = parseDirective(result.value)
356
+ if (directive == null) {
357
+ emit(
358
+ contractError("collaboration directive violates the frozen shape"),
359
+ eventGeneration
360
+ )
361
+ false
362
+ } else {
363
+ eventSink(
364
+ AndroidCollaborationTransportEvent.Directive(directive, eventGeneration, reason)
365
+ )
366
+ scheduleDeadline(directive.nextDeadlineMillis)
367
+ if (directive.generationToOpen != null) {
368
+ openSocket(directive.generationToOpen)
369
+ } else {
370
+ driveOutboundIfPossible()
371
+ }
372
+ true
373
+ }
374
+ }
375
+ }
376
+
377
+ private fun openSocket(newGeneration: String) {
378
+ val activeConfig = config ?: return
379
+ if (!canDrive()) return
380
+ retireNativeResources()
381
+ generation = newGeneration
382
+ closeReported = false
383
+ networkSocketOpened = false
384
+ socketOpened = false
385
+ protocolAttemptId =
386
+ if (activeConfig.protocolAdapter ==
387
+ null
388
+ ) {
389
+ null
390
+ } else {
391
+ java.util.UUID.randomUUID().toString()
392
+ }
393
+ protocolEventSequence = 0uL
394
+ pendingProtocolEventId = null
395
+ negotiatedProtocol = null
396
+ bufferedProtocolFrames.clear()
397
+ val token = ++socketToken
398
+ val newSocket = socketFactory.makeSocket(
399
+ activeConfig.url,
400
+ activeConfig.protocolAdapter?.protocols ?: emptyList(),
401
+ CollaborationSocketCallbacks(
402
+ onOpen = { selectedProtocol ->
403
+ enqueue { socketDidOpen(token, newGeneration, selectedProtocol) }
404
+ },
405
+ onBinaryMessage = { bytes ->
406
+ enqueue { socketDidReceive(token, newGeneration, bytes) }
407
+ },
408
+ onTextMessage = { text ->
409
+ enqueue { socketDidReceiveText(token, newGeneration, text) }
410
+ },
411
+ onClosing = {},
412
+ onClosed = { code ->
413
+ enqueue { socketDidClose(token, newGeneration, code) }
414
+ },
415
+ onFailure = {
416
+ enqueue { failCurrentSocket(token, newGeneration, null) }
417
+ }
418
+ )
419
+ )
420
+ socket = newSocket
421
+ newSocket.connect()
422
+ }
423
+
424
+ private fun socketDidOpen(token: Long, callbackGeneration: String, selectedProtocol: String?) {
425
+ if (!isCurrent(token, callbackGeneration) || networkSocketOpened) return
426
+ networkSocketOpened = true
427
+ negotiatedProtocol = selectedProtocol
428
+ if (config?.protocolAdapter == null) {
429
+ activateYjs(token, callbackGeneration)
430
+ return
431
+ }
432
+ scheduleProtocolAdapterTimeout(token, callbackGeneration)
433
+ emitProtocolAdapterEvent(
434
+ NativeCollaborationProtocolAdapterPhase.Open,
435
+ token,
436
+ callbackGeneration
437
+ )
438
+ }
439
+
440
+ private fun activateYjs(token: Long, callbackGeneration: String) {
441
+ if (
442
+ !isCurrent(token, callbackGeneration) ||
443
+ !networkSocketOpened ||
444
+ socketOpened
445
+ ) {
446
+ return
447
+ }
448
+ protocolAdapterDeadline?.cancel(false)
449
+ protocolAdapterDeadline = null
450
+ pendingProtocolEventId = null
451
+ socketOpened = true
452
+ val accepted = consumeDirective(
453
+ backend.collaborationSocketOpen(
454
+ editorId,
455
+ callbackGeneration,
456
+ clock.nowMillis().toString()
457
+ ),
458
+ callbackGeneration,
459
+ CollaborationWakeReason.OPEN
460
+ )
461
+ if (!accepted) failCurrentSocket(token, callbackGeneration, 1008)
462
+ }
463
+
464
+ private fun socketDidReceive(token: Long, callbackGeneration: String, bytes: ByteArray) {
465
+ if (!isCurrent(token, callbackGeneration)) return
466
+ if (!socketOpened) {
467
+ receiveProtocolAdapterFrame(
468
+ NativeCollaborationProtocolFrame.Binary(bytes),
469
+ token,
470
+ callbackGeneration
471
+ )
472
+ return
473
+ }
474
+ val accepted = consumeDirective(
475
+ backend.collaborationReceive(
476
+ editorId,
477
+ callbackGeneration,
478
+ bytes,
479
+ clock.nowMillis().toString()
480
+ ),
481
+ callbackGeneration,
482
+ CollaborationWakeReason.RECEIVE
483
+ )
484
+ if (!accepted) failCurrentSocket(token, callbackGeneration, 1008)
485
+ }
486
+
487
+ private fun socketDidReceiveText(token: Long, callbackGeneration: String, text: String) {
488
+ if (!isCurrent(token, callbackGeneration)) return
489
+ if (!socketOpened) {
490
+ receiveProtocolAdapterFrame(
491
+ NativeCollaborationProtocolFrame.Text(text),
492
+ token,
493
+ callbackGeneration
494
+ )
495
+ } else {
496
+ failCurrentSocket(token, callbackGeneration, 1008)
497
+ }
498
+ }
499
+
500
+ private fun driveOutboundIfPossible() {
501
+ val activeGeneration = generation ?: return
502
+ val activeSocket = socket ?: return
503
+ if (!canDrive() || !socketOpened || inFlightLease != null) return
504
+
505
+ when (val result = backend.collaborationLeaseOutbound(editorId, activeGeneration)) {
506
+ EditorV2LeaseResult.Empty -> Unit
507
+
508
+ is EditorV2LeaseResult.Err -> {
509
+ emit(result.error, activeGeneration)
510
+ failCurrentSocket(socketToken, activeGeneration, 1008)
511
+ }
512
+
513
+ is EditorV2LeaseResult.Value -> {
514
+ val lease = result.lease
515
+ inFlightLease = lease
516
+ if (activeSocket.send(lease.frame.toByteString())) {
517
+ inFlightLease = null
518
+ when (
519
+ val ack = backend.collaborationAckOutbound(
520
+ editorId,
521
+ activeGeneration,
522
+ lease.leaseId
523
+ )
524
+ ) {
525
+ is EditorV2CallResult.Err -> {
526
+ emit(ack.error, activeGeneration)
527
+ failCurrentSocket(socketToken, activeGeneration, 1008)
528
+ }
529
+
530
+ is EditorV2CallResult.Ok -> enqueue {
531
+ if (generation ==
532
+ activeGeneration
533
+ ) {
534
+ drive(CollaborationWakeReason.LOCAL_MUTATION)
535
+ }
536
+ }
537
+ }
538
+ } else {
539
+ inFlightLease = null
540
+ when (
541
+ val nack = backend.collaborationNackOutbound(
542
+ editorId,
543
+ activeGeneration,
544
+ lease.leaseId
545
+ )
546
+ ) {
547
+ is EditorV2CallResult.Err -> emit(nack.error, activeGeneration)
548
+ is EditorV2CallResult.Ok -> Unit
549
+ }
550
+ failCurrentSocket(socketToken, activeGeneration, null)
551
+ }
552
+ }
553
+ }
554
+ }
555
+
556
+ private fun socketDidClose(token: Long, callbackGeneration: String, code: Int) {
557
+ if (!isCurrent(token, callbackGeneration)) return
558
+ val backendCode =
559
+ if (config?.protocolAdapter?.terminalCloseCodes?.contains(code) == true) 1008 else code
560
+ reportCurrentSocketClose(callbackGeneration, backendCode)
561
+ }
562
+
563
+ private fun failCurrentSocket(token: Long, callbackGeneration: String, code: Int?) {
564
+ if (!isCurrent(token, callbackGeneration)) return
565
+ retireCurrentSocket()
566
+ reportCurrentSocketClose(callbackGeneration, code)
567
+ }
568
+
569
+ /** Peers reap presence on the close handshake; cancelling alone strands it. */
570
+ private fun retireCurrentSocket() {
571
+ val active = socket ?: return
572
+ if (!active.close(WEBSOCKET_CLOSE_GOING_AWAY, null)) active.cancel()
573
+ }
574
+
575
+ private fun reportCurrentSocketClose(callbackGeneration: String, code: Int?) {
576
+ if (closeReported) return
577
+ closeReported = true
578
+ deadline?.cancel(false)
579
+ deadline = null
580
+ protocolAdapterDeadline?.cancel(false)
581
+ protocolAdapterDeadline = null
582
+ socketToken += 1
583
+ socket = null
584
+ networkSocketOpened = false
585
+ socketOpened = false
586
+ protocolAttemptId = null
587
+ pendingProtocolEventId = null
588
+ negotiatedProtocol = null
589
+ bufferedProtocolFrames.clear()
590
+ inFlightLease = null
591
+ generation = null
592
+ consumeDirective(
593
+ backend.collaborationSocketClose(
594
+ editorId,
595
+ callbackGeneration,
596
+ code?.takeIf { it >= 0 }?.toUInt(),
597
+ null,
598
+ clock.nowMillis().toString()
599
+ ),
600
+ callbackGeneration,
601
+ CollaborationWakeReason.TIMER
602
+ )
603
+ }
604
+
605
+ private fun scheduleDeadline(rawDeadline: String?) {
606
+ deadline?.cancel(false)
607
+ deadline = null
608
+ val target = rawDeadline?.let(::canonicalULong) ?: return
609
+ if (!canDrive()) return
610
+ val now = clock.nowMillis().coerceAtLeast(0).toULong()
611
+ val delay = if (target > now) target - now else 0uL
612
+ val boundedDelay = minOf(delay, Long.MAX_VALUE.toULong()).toLong()
613
+ deadline = executor.schedule(
614
+ {
615
+ deadline = null
616
+ drive(CollaborationWakeReason.TIMER)
617
+ },
618
+ boundedDelay,
619
+ TimeUnit.MILLISECONDS
620
+ )
621
+ }
622
+
623
+ private fun scheduleProtocolAdapterTimeout(token: Long, callbackGeneration: String) {
624
+ protocolAdapterDeadline?.cancel(false)
625
+ val timeoutMillis = config?.protocolAdapter?.timeoutMillis ?: return
626
+ protocolAdapterDeadline = executor.schedule(
627
+ {
628
+ protocolAdapterDeadline = null
629
+ failCurrentSocket(token, callbackGeneration, 1008)
630
+ },
631
+ timeoutMillis,
632
+ TimeUnit.MILLISECONDS
633
+ )
634
+ }
635
+
636
+ private fun receiveProtocolAdapterFrame(
637
+ frame: NativeCollaborationProtocolFrame,
638
+ token: Long,
639
+ callbackGeneration: String
640
+ ) {
641
+ if (config?.protocolAdapter == null) {
642
+ failCurrentSocket(token, callbackGeneration, 1008)
643
+ return
644
+ }
645
+ val frameSize = when (frame) {
646
+ is NativeCollaborationProtocolFrame.Text ->
647
+ frame.data.toByteArray(Charsets.UTF_8).size
648
+
649
+ is NativeCollaborationProtocolFrame.Binary -> frame.data.size
650
+ }
651
+ if (frameSize > NativeCollaborationProtocolAdapterConfig.MAXIMUM_FRAME_BYTES) {
652
+ failCurrentSocket(token, callbackGeneration, 1008)
653
+ return
654
+ }
655
+ if (pendingProtocolEventId != null) {
656
+ if (
657
+ bufferedProtocolFrames.size >=
658
+ NativeCollaborationProtocolAdapterConfig.MAXIMUM_PRE_READY_FRAMES - 1
659
+ ) {
660
+ failCurrentSocket(token, callbackGeneration, 1008)
661
+ } else {
662
+ bufferedProtocolFrames.addLast(frame)
663
+ }
664
+ return
665
+ }
666
+ emitProtocolAdapterEvent(
667
+ NativeCollaborationProtocolAdapterPhase.Message(frame),
668
+ token,
669
+ callbackGeneration
670
+ )
671
+ }
672
+
673
+ private fun emitProtocolAdapterEvent(
674
+ phase: NativeCollaborationProtocolAdapterPhase,
675
+ token: Long,
676
+ callbackGeneration: String
677
+ ) {
678
+ val attemptId = protocolAttemptId
679
+ if (
680
+ !isCurrent(token, callbackGeneration) ||
681
+ config?.protocolAdapter == null ||
682
+ attemptId == null ||
683
+ pendingProtocolEventId != null ||
684
+ protocolEventSequence == ULong.MAX_VALUE
685
+ ) {
686
+ failCurrentSocket(token, callbackGeneration, 1008)
687
+ return
688
+ }
689
+ protocolEventSequence += 1uL
690
+ val eventId = protocolEventSequence.toString()
691
+ pendingProtocolEventId = eventId
692
+ eventSink(
693
+ AndroidCollaborationTransportEvent.ProtocolAdapter(
694
+ NativeCollaborationProtocolAdapterEvent(
695
+ attemptId = attemptId,
696
+ eventId = eventId,
697
+ generation = callbackGeneration,
698
+ negotiatedProtocol = negotiatedProtocol,
699
+ phase = phase
700
+ )
701
+ )
702
+ )
703
+ }
704
+
705
+ private fun emitNextBufferedProtocolFrame(token: Long, callbackGeneration: String) {
706
+ if (bufferedProtocolFrames.isEmpty()) return
707
+ val next = bufferedProtocolFrames.removeFirst()
708
+ emitProtocolAdapterEvent(
709
+ NativeCollaborationProtocolAdapterPhase.Message(next),
710
+ token,
711
+ callbackGeneration
712
+ )
713
+ }
714
+
715
+ private fun drainBufferedFramesAfterReady(token: Long, callbackGeneration: String) {
716
+ while (
717
+ bufferedProtocolFrames.isNotEmpty() &&
718
+ isCurrent(token, callbackGeneration) &&
719
+ socketOpened
720
+ ) {
721
+ when (val frame = bufferedProtocolFrames.removeFirst()) {
722
+ is NativeCollaborationProtocolFrame.Text -> {
723
+ failCurrentSocket(token, callbackGeneration, 1008)
724
+ }
725
+
726
+ is NativeCollaborationProtocolFrame.Binary -> {
727
+ socketDidReceive(token, callbackGeneration, frame.data)
728
+ }
729
+ }
730
+ }
731
+ }
732
+
733
+ private fun sendProtocolAdapterFrames(
734
+ activeSocket: CollaborationSocket,
735
+ frames: List<NativeCollaborationProtocolFrame>
736
+ ): Boolean = frames.all { frame ->
737
+ when (frame) {
738
+ is NativeCollaborationProtocolFrame.Text -> activeSocket.send(frame.data)
739
+
740
+ is NativeCollaborationProtocolFrame.Binary ->
741
+ activeSocket.send(frame.data.toByteString())
742
+ }
743
+ }
744
+
745
+ private fun retireNativeResources() {
746
+ deadline?.cancel(false)
747
+ deadline = null
748
+ protocolAdapterDeadline?.cancel(false)
749
+ protocolAdapterDeadline = null
750
+ socketToken += 1
751
+ retireCurrentSocket()
752
+ socket = null
753
+ generation = null
754
+ inFlightLease = null
755
+ networkSocketOpened = false
756
+ socketOpened = false
757
+ protocolAttemptId = null
758
+ pendingProtocolEventId = null
759
+ negotiatedProtocol = null
760
+ bufferedProtocolFrames.clear()
761
+ closeReported = true
762
+ }
763
+
764
+ private fun isCurrent(token: Long, callbackGeneration: String): Boolean =
765
+ !destroyed && !terminal.get() && token == socketToken && generation == callbackGeneration
766
+
767
+ private fun parseDirective(json: String): AndroidCollaborationDirective? {
768
+ val objectValue = runCatching { JSONObject(json) }.getOrNull() ?: return null
769
+ val generationToOpen =
770
+ nullableCanonicalString(objectValue, "generationToOpen") ?: return null
771
+ val nextDeadlineMillis =
772
+ nullableCanonicalString(objectValue, "nextDeadlineMillis") ?: return null
773
+ val expired = objectValue.optJSONArray("expiredPeers") ?: return null
774
+ val expiredPeers = buildList {
775
+ for (index in 0 until expired.length()) {
776
+ val value = expired.opt(index) as? String ?: return null
777
+ if (canonicalULong(value) == null) return null
778
+ add(value)
779
+ }
780
+ }
781
+ return runCatching {
782
+ AndroidCollaborationDirective(
783
+ transportState = objectValue.getString("transportState"),
784
+ generationToOpen = generationToOpen.value,
785
+ nextDeadlineMillis = nextDeadlineMillis.value,
786
+ remoteCommitApplied = objectValue.getBoolean("remoteCommitApplied"),
787
+ peersChanged = objectValue.getBoolean("peersChanged"),
788
+ renewedLocal = objectValue.getBoolean("renewedLocal"),
789
+ expiredPeers = expiredPeers
790
+ )
791
+ }.getOrNull()
792
+ }
793
+
794
+ private data class NullableCanonicalString(val value: String?)
795
+
796
+ private fun nullableCanonicalString(value: JSONObject, key: String): NullableCanonicalString? {
797
+ if (!value.has(key)) return null
798
+ if (value.isNull(key)) return NullableCanonicalString(null)
799
+ val raw = value.opt(key) as? String ?: return null
800
+ if (canonicalULong(raw) == null) return null
801
+ return NullableCanonicalString(raw)
802
+ }
803
+
804
+ private fun canonicalULong(raw: String): ULong? {
805
+ if (raw.isEmpty() || raw.any { it !in '0'..'9' }) return null
806
+ if (raw.length > 1 && raw.first() == '0') return null
807
+ return raw.toULongOrNull()
808
+ }
809
+
810
+ private fun contractError(message: String) =
811
+ EditorV2Error("boundary", "FFI_RESULT_INVALID", message)
812
+
813
+ private fun lifecycleError(message: String) =
814
+ EditorV2Error("lifecycle", "ENGINE_DESTROYED", message)
815
+
816
+ private fun emit(error: EditorV2Error, eventGeneration: String? = generation) {
817
+ eventSink(AndroidCollaborationTransportEvent.Error(error, eventGeneration))
818
+ }
819
+
820
+ private fun enqueue(operation: () -> Unit) {
821
+ if (terminal.get() || executor.isShutdown) return
822
+ runCatching { executor.execute(operation) }
823
+ }
824
+
825
+ private fun <T> onWorker(rejected: () -> T, operation: () -> T): T {
826
+ if (Thread.currentThread() === workerThread.get()) return operation()
827
+ val future: Future<T> = synchronized(submissionLock) {
828
+ if (terminal.get() || executor.isShutdown) return rejected()
829
+ try {
830
+ executor.submit<T> { operation() }
831
+ } catch (_: RejectedExecutionException) {
832
+ return rejected()
833
+ }
834
+ }
835
+ return future.get()
836
+ }
837
+
838
+ internal fun enqueueForTesting(operation: () -> Unit) {
839
+ executor.execute(operation)
840
+ }
841
+
842
+ internal fun awaitIdleForTesting() {
843
+ onWorker({ Unit }) { Unit }
844
+ }
845
+
846
+ internal fun awaitDestroyedForTesting(): Boolean =
847
+ runCatching { destroyedFuture.get(2, TimeUnit.SECONDS) }.isSuccess
848
+
849
+ internal fun hostStateForTesting(): HostState = onWorker({
850
+ desiredHostState.get()
851
+ }) { hostState }
852
+
853
+ internal fun configForTesting(): NativeCollaborationTransportConfig? = onWorker({
854
+ null
855
+ }) { config }
856
+
857
+ internal companion object {
858
+ const val WEBSOCKET_CLOSE_GOING_AWAY = 1001
859
+ }
860
+ }