@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,103 @@
1
+ import Foundation
2
+
3
+ enum EditorOrderedListNumberingScheme: String {
4
+ case decimal
5
+ case lowerAlpha
6
+ case upperAlpha
7
+ case lowerRoman
8
+ case upperRoman
9
+ }
10
+
11
+ struct EditorOrderedListMarkerTheme {
12
+ static let defaultSchemes: [EditorOrderedListNumberingScheme] = [
13
+ .decimal,
14
+ .lowerAlpha,
15
+ .lowerRoman
16
+ ]
17
+
18
+ let schemes: [EditorOrderedListNumberingScheme]
19
+ let suffix: String
20
+
21
+ init(dictionary: [String: Any]) {
22
+ let parsed = (dictionary["schemes"] as? [Any])?.compactMap { value in
23
+ (value as? String).flatMap(EditorOrderedListNumberingScheme.init(rawValue:))
24
+ } ?? []
25
+ schemes = parsed.isEmpty ? Self.defaultSchemes : parsed
26
+ let parsedSuffix = dictionary["suffix"] as? String
27
+ suffix = parsedSuffix == ")" ? ")" : "."
28
+ }
29
+ }
30
+
31
+ enum OrderedListMarkerFormatter {
32
+ static func label(
33
+ index: UInt32,
34
+ nestingDepth: Int,
35
+ theme: EditorOrderedListMarkerTheme?
36
+ ) -> String {
37
+ let resolved = theme ?? EditorOrderedListMarkerTheme(dictionary: [:])
38
+ let depth = max(0, nestingDepth)
39
+ let scheme = resolved.schemes[depth % resolved.schemes.count]
40
+ return formattedIndex(index, scheme: scheme) + resolved.suffix
41
+ }
42
+
43
+ private static func formattedIndex(
44
+ _ index: UInt32,
45
+ scheme: EditorOrderedListNumberingScheme
46
+ ) -> String {
47
+ switch scheme {
48
+ case .decimal:
49
+ return String(index)
50
+ case .lowerAlpha:
51
+ return alphabeticIndex(index) ?? String(index)
52
+ case .upperAlpha:
53
+ return alphabeticIndex(index)?.uppercased() ?? String(index)
54
+ case .lowerRoman:
55
+ return romanIndex(index) ?? String(index)
56
+ case .upperRoman:
57
+ return romanIndex(index)?.uppercased() ?? String(index)
58
+ }
59
+ }
60
+
61
+ private static func alphabeticIndex(_ index: UInt32) -> String? {
62
+ guard index > 0 else { return nil }
63
+
64
+ var value = index
65
+ var characters: [String] = []
66
+ while value > 0 {
67
+ let offset = (value - 1) % 26
68
+ guard let scalar = UnicodeScalar(97 + offset) else { return nil }
69
+ characters.append(String(scalar))
70
+ value = (value - 1) / 26
71
+ }
72
+ return characters.reversed().joined()
73
+ }
74
+
75
+ private static func romanIndex(_ index: UInt32) -> String? {
76
+ guard index > 0, index <= 3_999 else { return nil }
77
+
78
+ let table: [(value: UInt32, symbol: String)] = [
79
+ (1_000, "m"),
80
+ (900, "cm"),
81
+ (500, "d"),
82
+ (400, "cd"),
83
+ (100, "c"),
84
+ (90, "xc"),
85
+ (50, "l"),
86
+ (40, "xl"),
87
+ (10, "x"),
88
+ (9, "ix"),
89
+ (5, "v"),
90
+ (4, "iv"),
91
+ (1, "i")
92
+ ]
93
+ var value = index
94
+ var result = ""
95
+ for entry in table {
96
+ while value >= entry.value {
97
+ result += entry.symbol
98
+ value -= entry.value
99
+ }
100
+ }
101
+ return result
102
+ }
103
+ }
@@ -0,0 +1,596 @@
1
+ import ObjectiveC
2
+ import UIKit
3
+
4
+ // MARK: - PositionBridge
5
+
6
+ /// Converts between UITextView cursor positions (UTF-16 code unit offsets, snapped
7
+ /// to grapheme cluster boundaries) and Rust editor-core scalar offsets (Unicode
8
+ /// scalar values = Unicode code points).
9
+ ///
10
+ /// UIKit's text system uses UTF-16 internally (NSString). Emoji like U+1F468
11
+ /// (man) occupy 2 UTF-16 code units (a surrogate pair) but 1 Unicode scalar.
12
+ /// Composed emoji sequences like 👨‍👩‍👧‍👦 are multiple scalars joined by
13
+ /// ZWJ but render as a single grapheme cluster.
14
+ ///
15
+ /// Rust's editor-core counts positions in Unicode scalars (what Rust calls `char`).
16
+ /// The PositionMap in Rust converts between doc positions and scalar offsets.
17
+ /// This bridge converts between those scalar offsets and UITextView UTF-16 offsets.
18
+ final class PositionBridge {
19
+
20
+ private struct StringConversionTable {
21
+ let utf16ToScalar: [UInt32]
22
+ let scalarToUtf16: [Int]
23
+ }
24
+
25
+ private final class TextViewConversionTable: NSObject {
26
+ let adjustedUtf16ToScalar: [UInt32]
27
+
28
+ init(adjustedUtf16ToScalar: [UInt32]) {
29
+ self.adjustedUtf16ToScalar = adjustedUtf16ToScalar
30
+ }
31
+ }
32
+
33
+ struct VirtualListMarker {
34
+ let paragraphStartUtf16: Int
35
+ let scalarLength: UInt32
36
+ }
37
+
38
+ private struct PositionAdjustments {
39
+ let placeholders: [Int]
40
+ let listMarkers: [VirtualListMarker]
41
+ }
42
+
43
+ private static var textViewConversionTableKey: UInt8 = 0
44
+ private static let stringTableLock = NSLock()
45
+ private static var lastStringTableText = ""
46
+ private static var lastStringTable: StringConversionTable?
47
+
48
+ // MARK: - UTF-16 <-> Scalar Conversion
49
+
50
+ /// Convert a UITextView cursor position (UTF-16 offset) to a Rust scalar offset.
51
+ ///
52
+ /// Walks the string from the beginning, counting Unicode scalars consumed as
53
+ /// we advance through UTF-16 code units. Surrogate pairs (code units > U+FFFF)
54
+ /// contribute 2 UTF-16 code units but only 1 scalar.
55
+ ///
56
+ /// - Parameters:
57
+ /// - position: A `UITextPosition` obtained from the text view.
58
+ /// - textView: The text view containing the text.
59
+ /// - Returns: The equivalent Unicode scalar offset.
60
+ static func textViewToScalar(_ position: UITextPosition, in textView: UITextView) -> UInt32 {
61
+ let utf16Offset = textView.offset(from: textView.beginningOfDocument, to: position)
62
+ return utf16OffsetToScalar(utf16Offset, in: textView)
63
+ }
64
+
65
+ /// Convert a Rust scalar offset to a UITextView position.
66
+ ///
67
+ /// Walks the string counting scalars until we reach the target, then returns
68
+ /// the corresponding UTF-16 offset as a UITextPosition.
69
+ ///
70
+ /// - Parameters:
71
+ /// - scalar: The Unicode scalar offset from Rust.
72
+ /// - textView: The text view containing the text.
73
+ /// - Returns: The equivalent `UITextPosition`, or the end of document if the
74
+ /// scalar offset exceeds the text length.
75
+ static func scalarToTextView(_ scalar: UInt32, in textView: UITextView) -> UITextPosition {
76
+ let utf16Offset = scalarToUtf16Offset(scalar, in: textView)
77
+ return textView.position(
78
+ from: textView.beginningOfDocument,
79
+ offset: utf16Offset
80
+ ) ?? textView.endOfDocument
81
+ }
82
+
83
+ static func utf16OffsetToScalar(_ utf16Offset: Int, in textView: UITextView) -> UInt32 {
84
+ let text = textView.text ?? ""
85
+ let conversionTable = textViewConversionTable(for: textView)
86
+ guard !conversionTable.adjustedUtf16ToScalar.isEmpty else { return 0 }
87
+ let clampedOffset = min(
88
+ max(utf16Offset, 0),
89
+ min((text as NSString).length, conversionTable.adjustedUtf16ToScalar.count - 1)
90
+ )
91
+ return conversionTable.adjustedUtf16ToScalar[clampedOffset]
92
+ }
93
+
94
+ static func utf16OffsetToScalar(_ utf16Offset: Int, in attributedString: NSAttributedString) -> UInt32 {
95
+ let conversionTable = adjustedConversionTable(for: attributedString)
96
+ guard !conversionTable.isEmpty else { return 0 }
97
+ let clampedOffset = min(max(utf16Offset, 0), conversionTable.count - 1)
98
+ return conversionTable[clampedOffset]
99
+ }
100
+
101
+ static func scalarToUtf16Offset(_ scalar: UInt32, in textView: UITextView) -> Int {
102
+ let conversionTable = textViewConversionTable(for: textView)
103
+ let utf16ToScalar = conversionTable.adjustedUtf16ToScalar
104
+ return scalarToUtf16Offset(scalar, inAdjustedUtf16ToScalarTable: utf16ToScalar)
105
+ }
106
+
107
+ static func scalarToUtf16Offset(_ scalar: UInt32, in attributedString: NSAttributedString) -> Int {
108
+ let utf16ToScalar = adjustedConversionTable(for: attributedString)
109
+ return scalarToUtf16Offset(scalar, inAdjustedUtf16ToScalarTable: utf16ToScalar)
110
+ }
111
+
112
+ private static func scalarToUtf16Offset(
113
+ _ scalar: UInt32,
114
+ inAdjustedUtf16ToScalarTable utf16ToScalar: [UInt32]
115
+ ) -> Int {
116
+ guard scalar > 0, !utf16ToScalar.isEmpty else {
117
+ return 0
118
+ }
119
+
120
+ if let last = utf16ToScalar.last, scalar > last {
121
+ return utf16ToScalar.count - 1
122
+ }
123
+
124
+ var low = 0
125
+ var high = utf16ToScalar.count - 1
126
+ while low < high {
127
+ let mid = (low + high) / 2
128
+ if utf16ToScalar[mid] < scalar {
129
+ low = mid + 1
130
+ } else {
131
+ high = mid
132
+ }
133
+ }
134
+
135
+ return low
136
+ }
137
+
138
+ /// Convert a UTF-16 offset to a Unicode scalar offset within a string.
139
+ ///
140
+ /// This is the core conversion used by `textViewToScalar`. Exposed as a
141
+ /// static method for direct use and testing.
142
+ ///
143
+ /// - Parameters:
144
+ /// - utf16Offset: The UTF-16 code unit offset.
145
+ /// - text: The string to walk.
146
+ /// - Returns: The number of Unicode scalars from the start to the given UTF-16 offset.
147
+ static func utf16OffsetToScalar(_ utf16Offset: Int, in text: String) -> UInt32 {
148
+ let conversionTable = stringConversionTable(for: text)
149
+ let clampedOffset = min(max(utf16Offset, 0), conversionTable.utf16ToScalar.count - 1)
150
+ return conversionTable.utf16ToScalar[clampedOffset]
151
+ }
152
+
153
+ /// Convert a Unicode scalar offset to a UTF-16 offset within a string.
154
+ ///
155
+ /// This is the core conversion used by `scalarToTextView`. Exposed as a
156
+ /// static method for direct use and testing.
157
+ ///
158
+ /// - Parameters:
159
+ /// - scalar: The Unicode scalar offset.
160
+ /// - text: The string to walk.
161
+ /// - Returns: The number of UTF-16 code units from the start to the given scalar offset.
162
+ static func scalarToUtf16Offset(_ scalar: UInt32, in text: String) -> Int {
163
+ let conversionTable = stringConversionTable(for: text)
164
+ guard scalar > 0 else { return 0 }
165
+ let scalarIndex = min(Int(scalar), conversionTable.scalarToUtf16.count - 1)
166
+ return conversionTable.scalarToUtf16[scalarIndex]
167
+ }
168
+
169
+ // MARK: - Grapheme Boundary Snapping
170
+
171
+ /// Snap a UTF-16 offset to the nearest grapheme cluster boundary.
172
+ ///
173
+ /// UITextView may report offsets in the middle of a grapheme cluster (e.g.
174
+ /// between the scalars of a flag emoji or a composed character sequence).
175
+ /// This method snaps the offset forward to the end of the current grapheme
176
+ /// cluster, since that is the position the user would perceive.
177
+ ///
178
+ /// - Parameters:
179
+ /// - utf16Offset: A UTF-16 code unit offset that may be mid-grapheme.
180
+ /// - text: The string to inspect.
181
+ /// - Returns: The nearest grapheme-aligned UTF-16 offset. If the input is
182
+ /// already on a boundary, it is returned unchanged.
183
+ static func snapToGraphemeBoundary(_ utf16Offset: Int, in text: String) -> Int {
184
+ guard !text.isEmpty else { return 0 }
185
+
186
+ let nsString = text as NSString
187
+ let clampedOffset = min(max(utf16Offset, 0), nsString.length)
188
+
189
+ if clampedOffset == 0 || clampedOffset == nsString.length {
190
+ return clampedOffset
191
+ }
192
+
193
+ // composedCharacterSequence(at:) returns the full grapheme cluster range
194
+ // containing the given UTF-16 index. We snap to the end of that range
195
+ // (forward bias) since that's what a user moving the cursor expects.
196
+ let range = nsString.rangeOfComposedCharacterSequence(at: clampedOffset)
197
+
198
+ if range.location == clampedOffset {
199
+ return clampedOffset
200
+ }
201
+
202
+ return NSMaxRange(range)
203
+ }
204
+
205
+ // MARK: - UITextRange <-> Scalar Range
206
+
207
+ /// Convert a UITextRange to a (from, to) pair of Rust scalar offsets.
208
+ ///
209
+ /// - Parameters:
210
+ /// - range: A `UITextRange` from the text view.
211
+ /// - textView: The text view containing the text.
212
+ /// - Returns: A tuple of (from, to) scalar offsets where from <= to.
213
+ static func textRangeToScalarRange(
214
+ _ range: UITextRange,
215
+ in textView: UITextView
216
+ ) -> (from: UInt32, to: UInt32) {
217
+ let from = textViewToScalar(range.start, in: textView)
218
+ let to = textViewToScalar(range.end, in: textView)
219
+ return (from: min(from, to), to: max(from, to))
220
+ }
221
+
222
+ /// Convert a pair of Rust scalar offsets to a UITextRange.
223
+ ///
224
+ /// - Parameters:
225
+ /// - from: The start scalar offset.
226
+ /// - to: The end scalar offset.
227
+ /// - textView: The text view.
228
+ /// - Returns: The corresponding `UITextRange`, or nil if the positions are invalid.
229
+ static func scalarRangeToTextRange(
230
+ from: UInt32,
231
+ to: UInt32,
232
+ in textView: UITextView
233
+ ) -> UITextRange? {
234
+ let startPos = scalarToTextView(from, in: textView)
235
+ let endPos = scalarToTextView(to, in: textView)
236
+ return textView.textRange(from: startPos, to: endPos)
237
+ }
238
+
239
+ // MARK: - Cursor Scalar Offset (Convenience)
240
+
241
+ /// Get the current cursor position as a Rust scalar offset.
242
+ ///
243
+ /// If there is a range selection, returns the head (moving end) position.
244
+ ///
245
+ /// - Parameter textView: The text view.
246
+ /// - Returns: The scalar offset of the cursor, or 0 if no selection exists.
247
+ static func cursorScalarOffset(in textView: UITextView) -> UInt32 {
248
+ if let editorTextView = textView as? EditorTextView,
249
+ let selection = editorTextView.currentLogicalScalarSelection() {
250
+ return selection.head
251
+ }
252
+ guard let selectedRange = textView.selectedTextRange else { return 0 }
253
+ return textViewToScalar(selectedRange.end, in: textView)
254
+ }
255
+
256
+ static func virtualListMarker(
257
+ atUtf16Offset utf16Offset: Int,
258
+ in textView: UITextView
259
+ ) -> VirtualListMarker? {
260
+ virtualListMarkers(in: textView.textStorage).first { $0.paragraphStartUtf16 == utf16Offset }
261
+ }
262
+
263
+ static func invalidateCache(for textView: UITextView) {
264
+ objc_setAssociatedObject(
265
+ textView,
266
+ &textViewConversionTableKey,
267
+ nil,
268
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
269
+ )
270
+ }
271
+
272
+ @discardableResult
273
+ static func applyAttributedPatchIfPossible(
274
+ for textView: UITextView,
275
+ replaceRange: NSRange,
276
+ replacement: NSAttributedString
277
+ ) -> Bool {
278
+ guard let cached = objc_getAssociatedObject(textView, &textViewConversionTableKey) as? TextViewConversionTable else {
279
+ return false
280
+ }
281
+
282
+ let oldAdjusted = cached.adjustedUtf16ToScalar
283
+ let oldUtf16Count = max(0, oldAdjusted.count - 1)
284
+ guard replaceRange.location >= 0,
285
+ replaceRange.length >= 0,
286
+ replaceRange.location + replaceRange.length <= oldUtf16Count
287
+ else {
288
+ return false
289
+ }
290
+
291
+ let startOffset = replaceRange.location
292
+ let endOffset = replaceRange.location + replaceRange.length
293
+ let replacementAdjusted = adjustedConversionTable(for: replacement)
294
+ let patched = patchedAdjustedConversionTable(
295
+ oldAdjusted: oldAdjusted,
296
+ startOffset: startOffset,
297
+ endOffset: endOffset,
298
+ replacementAdjusted: replacementAdjusted
299
+ )
300
+
301
+ objc_setAssociatedObject(
302
+ textView,
303
+ &textViewConversionTableKey,
304
+ TextViewConversionTable(adjustedUtf16ToScalar: patched),
305
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
306
+ )
307
+ return true
308
+ }
309
+
310
+ @discardableResult
311
+ static func applyPlainTextPatchIfPossible(
312
+ for textView: UITextView,
313
+ replaceRange: NSRange,
314
+ replacementText: String
315
+ ) -> Bool {
316
+ guard let cached = objc_getAssociatedObject(textView, &textViewConversionTableKey) as? TextViewConversionTable else {
317
+ return false
318
+ }
319
+
320
+ let oldAdjusted = cached.adjustedUtf16ToScalar
321
+ let oldUtf16Count = max(0, oldAdjusted.count - 1)
322
+ guard replaceRange.location >= 0,
323
+ replaceRange.length >= 0,
324
+ replaceRange.location + replaceRange.length <= oldUtf16Count
325
+ else {
326
+ return false
327
+ }
328
+
329
+ let startOffset = replaceRange.location
330
+ let endOffset = replaceRange.location + replaceRange.length
331
+ let replacementBase = stringConversionTable(for: replacementText).utf16ToScalar
332
+ let patched = patchedAdjustedConversionTable(
333
+ oldAdjusted: oldAdjusted,
334
+ startOffset: startOffset,
335
+ endOffset: endOffset,
336
+ replacementAdjusted: replacementBase
337
+ )
338
+
339
+ objc_setAssociatedObject(
340
+ textView,
341
+ &textViewConversionTableKey,
342
+ TextViewConversionTable(adjustedUtf16ToScalar: patched),
343
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
344
+ )
345
+ return true
346
+ }
347
+
348
+ private static func patchedAdjustedConversionTable(
349
+ oldAdjusted: [UInt32],
350
+ startOffset: Int,
351
+ endOffset: Int,
352
+ replacementAdjusted: [UInt32]
353
+ ) -> [UInt32] {
354
+ let startScalar = Int32(oldAdjusted[startOffset])
355
+ let deletedScalarCount = Int32(oldAdjusted[endOffset]) - startScalar
356
+ let replacementScalarCount = Int32(replacementAdjusted.last ?? 0)
357
+ let scalarDelta = replacementScalarCount - deletedScalarCount
358
+ let replacement = replacementAdjusted.map { value in
359
+ UInt32(max(0, Int32(value) + startScalar))
360
+ }
361
+ let prefix = Array(oldAdjusted[..<startOffset])
362
+ let suffix = oldAdjusted[(endOffset + 1)...].map { value in
363
+ UInt32(max(0, Int32(value) + scalarDelta))
364
+ }
365
+ return prefix + replacement + suffix
366
+ }
367
+
368
+ private static func stringConversionTable(for text: String) -> StringConversionTable {
369
+ stringTableLock.lock()
370
+ if lastStringTableText == text, let lastStringTable {
371
+ stringTableLock.unlock()
372
+ return lastStringTable
373
+ }
374
+ stringTableLock.unlock()
375
+
376
+ let utf16Count = text.utf16.count
377
+ let scalarCount = text.unicodeScalars.count
378
+ var utf16ToScalar = Array(repeating: UInt32(0), count: utf16Count + 1)
379
+ var scalarToUtf16 = Array(repeating: 0, count: scalarCount + 1)
380
+ var utf16Pos = 0
381
+ var scalarPos = 0
382
+
383
+ for scalar in text.unicodeScalars {
384
+ let nextUtf16Pos = utf16Pos + scalar.utf16.count
385
+ scalarPos += 1
386
+ if nextUtf16Pos > utf16Pos {
387
+ for offset in (utf16Pos + 1)...nextUtf16Pos {
388
+ utf16ToScalar[offset] = UInt32(scalarPos)
389
+ }
390
+ }
391
+ scalarToUtf16[scalarPos] = nextUtf16Pos
392
+ utf16Pos = nextUtf16Pos
393
+ }
394
+
395
+ let conversionTable = StringConversionTable(
396
+ utf16ToScalar: utf16ToScalar,
397
+ scalarToUtf16: scalarToUtf16
398
+ )
399
+
400
+ stringTableLock.lock()
401
+ lastStringTableText = text
402
+ lastStringTable = conversionTable
403
+ stringTableLock.unlock()
404
+
405
+ return conversionTable
406
+ }
407
+
408
+ private static func adjustedConversionTable(for attributedString: NSAttributedString) -> [UInt32] {
409
+ let baseTable = stringConversionTable(for: attributedString.string)
410
+ let adjustments = positionAdjustments(in: attributedString)
411
+ return adjustedUtf16ToScalar(
412
+ baseUtf16ToScalar: baseTable.utf16ToScalar,
413
+ placeholders: adjustments.placeholders,
414
+ listMarkers: adjustments.listMarkers
415
+ )
416
+ }
417
+
418
+ private static func textViewConversionTable(for textView: UITextView) -> TextViewConversionTable {
419
+ if let cached = objc_getAssociatedObject(textView, &textViewConversionTableKey) as? TextViewConversionTable {
420
+ return cached
421
+ }
422
+
423
+ let text = textView.text ?? ""
424
+ let baseTable = stringConversionTable(for: text)
425
+ let adjustments = positionAdjustments(in: textView.textStorage)
426
+ let adjustedUtf16ToScalar = adjustedUtf16ToScalar(
427
+ baseUtf16ToScalar: baseTable.utf16ToScalar,
428
+ placeholders: adjustments.placeholders,
429
+ listMarkers: adjustments.listMarkers
430
+ )
431
+ let conversionTable = TextViewConversionTable(adjustedUtf16ToScalar: adjustedUtf16ToScalar)
432
+ objc_setAssociatedObject(
433
+ textView,
434
+ &textViewConversionTableKey,
435
+ conversionTable,
436
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
437
+ )
438
+ return conversionTable
439
+ }
440
+
441
+ private static func adjustedUtf16ToScalar(
442
+ baseUtf16ToScalar: [UInt32],
443
+ placeholders: [Int],
444
+ listMarkers: [VirtualListMarker] = [],
445
+ ) -> [UInt32] {
446
+ let utf16Count = max(0, baseUtf16ToScalar.count - 1)
447
+ var deltas = Array(repeating: Int32(0), count: utf16Count + 2)
448
+
449
+ for placeholderOffset in placeholders {
450
+ let startOffset = min(max(placeholderOffset + 1, 0), utf16Count + 1)
451
+ if startOffset <= utf16Count {
452
+ deltas[startOffset] -= 1
453
+ }
454
+ }
455
+
456
+ for marker in listMarkers {
457
+ let startOffset = min(max(marker.paragraphStartUtf16, 0), utf16Count)
458
+ deltas[startOffset] += Int32(marker.scalarLength)
459
+ }
460
+
461
+ var adjustedUtf16ToScalar = Array(repeating: UInt32(0), count: utf16Count + 1)
462
+ var runningDelta: Int32 = 0
463
+ for offset in 0...utf16Count {
464
+ runningDelta += deltas[offset]
465
+ let adjustedValue = Int32(baseUtf16ToScalar[offset]) + runningDelta
466
+ adjustedUtf16ToScalar[offset] = UInt32(max(0, adjustedValue))
467
+ }
468
+ return adjustedUtf16ToScalar
469
+ }
470
+
471
+ private static func adjustedUtf16ToScalar(
472
+ baseUtf16ToScalar: [UInt32],
473
+ listMarkers: [VirtualListMarker]
474
+ ) -> [UInt32] {
475
+ adjustedUtf16ToScalar(baseUtf16ToScalar: baseUtf16ToScalar, placeholders: [], listMarkers: listMarkers)
476
+ }
477
+
478
+ private static func virtualListMarkers(in attributedString: NSAttributedString) -> [VirtualListMarker] {
479
+ positionAdjustments(in: attributedString).listMarkers
480
+ }
481
+
482
+ private static func positionAdjustments(in attributedString: NSAttributedString) -> PositionAdjustments {
483
+ guard attributedString.length > 0 else {
484
+ return PositionAdjustments(placeholders: [], listMarkers: [])
485
+ }
486
+
487
+ let nsString = attributedString.string as NSString
488
+ var placeholders: [Int] = []
489
+ var markers: [VirtualListMarker] = []
490
+ var seenStarts = Set<Int>()
491
+ let fullRange = NSRange(location: 0, length: attributedString.length)
492
+
493
+ attributedString.enumerateAttributes(
494
+ in: fullRange,
495
+ options: [.longestEffectiveRangeNotRequired]
496
+ ) { attrs, range, _ in
497
+ guard range.length > 0 else { return }
498
+
499
+ if attrs[RenderBridgeAttributes.syntheticPlaceholder] as? Bool == true {
500
+ placeholders.append(range.location)
501
+ }
502
+
503
+ guard let listContext = attrs[RenderBridgeAttributes.listMarkerContext] as? [String: Any] else {
504
+ return
505
+ }
506
+
507
+ let paragraphStart = nsString.paragraphRange(
508
+ for: NSRange(location: range.location, length: 0)
509
+ ).location
510
+ guard !isParagraphStartCreatedByHardBreak(
511
+ paragraphStart,
512
+ in: attributedString
513
+ ) else {
514
+ return
515
+ }
516
+ guard seenStarts.insert(paragraphStart).inserted else { return }
517
+
518
+ let markerLength = UInt32(
519
+ RenderBridge.listMarkerString(listContext: listContext).unicodeScalars.count
520
+ )
521
+ markers.append(
522
+ VirtualListMarker(
523
+ paragraphStartUtf16: paragraphStart,
524
+ scalarLength: markerLength
525
+ )
526
+ )
527
+ }
528
+
529
+ return PositionAdjustments(
530
+ placeholders: placeholders,
531
+ listMarkers: markers.sorted { $0.paragraphStartUtf16 < $1.paragraphStartUtf16 }
532
+ )
533
+ }
534
+
535
+ private static func virtualListMarkers(in textStorage: NSTextStorage) -> [VirtualListMarker] {
536
+ virtualListMarkers(in: textStorage as NSAttributedString)
537
+ }
538
+
539
+ private static func syntheticPlaceholderOffsets(in attributedString: NSAttributedString) -> [Int] {
540
+ positionAdjustments(in: attributedString).placeholders
541
+ }
542
+
543
+ private static func syntheticPlaceholderOffsets(in textStorage: NSTextStorage) -> [Int] {
544
+ syntheticPlaceholderOffsets(in: textStorage as NSAttributedString)
545
+ }
546
+
547
+ private static func isParagraphStartCreatedByHardBreak(
548
+ _ paragraphStart: Int,
549
+ in attributedString: NSAttributedString
550
+ ) -> Bool {
551
+ guard paragraphStart > 0, paragraphStart <= attributedString.length else { return false }
552
+ let previousVoidType = attributedString.attribute(
553
+ RenderBridgeAttributes.voidNodeType,
554
+ at: paragraphStart - 1,
555
+ effectiveRange: nil
556
+ ) as? String
557
+ return EditorNodeTypes.isHardBreak(previousVoidType)
558
+ }
559
+ }
560
+
561
+ // MARK: - v2 position envelopes
562
+ //
563
+ // The v2 native transaction bridge addresses positions as scalar/utf16
564
+ // offsets with an optional affinity. These helpers build the data-only
565
+ // envelope shapes (`native_transaction_bridge.rs`: PositionEnvelope,
566
+ // RangeEnvelope, SelectionEnvelope) from the native view's scalar currency.
567
+ enum EditorV2PositionBridge {
568
+ static func positionEnvelope(scalar: UInt32, affinity: String? = nil) -> [String: Any] {
569
+ var envelope: [String: Any] = ["offset": Int(scalar), "kind": "scalar"]
570
+ if let affinity {
571
+ envelope["affinity"] = affinity
572
+ }
573
+ return envelope
574
+ }
575
+
576
+ static func rangeEnvelope(from: UInt32, to: UInt32) -> [String: Any] {
577
+ [
578
+ "from": positionEnvelope(scalar: from),
579
+ "to": positionEnvelope(scalar: to)
580
+ ]
581
+ }
582
+
583
+ static func textSelectionEnvelope(anchor: UInt32, head: UInt32, affinity: String? = nil) -> [String: Any] {
584
+ [
585
+ "type": "text",
586
+ "anchor": positionEnvelope(scalar: anchor, affinity: affinity),
587
+ "head": positionEnvelope(scalar: head, affinity: affinity)
588
+ ]
589
+ }
590
+
591
+ /// Unicode-scalar length of one committed string (the position currency
592
+ /// used by every v2 envelope — NOT grapheme clusters, NOT UTF-16 units).
593
+ static func scalarLength(of text: String) -> UInt32 {
594
+ UInt32(text.unicodeScalars.count)
595
+ }
596
+ }