@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,635 @@
1
+ import CoreText
2
+ import UIKit
3
+
4
+ let preparedAtomAttribute = NSAttributedString.Key("PREPPreparedAtom")
5
+ /// Core Text has no strikethrough attribute. This marks a shaped run so its
6
+ /// immutable strike rectangle can be prepared from Core Text's own metrics.
7
+ let preparedStrikeAttribute = NSAttributedString.Key("PREPPreparedStrike")
8
+
9
+ final class CoreTextProseLayoutEngine {
10
+ /// UIFont and CTFont are toll-free bridged. Recreating a system font from
11
+ /// UIFont.fontName is not equivalent on current iOS releases: private
12
+ /// .SFUI names are not valid public Core Text PostScript names.
13
+ static func coreTextFont(from font: UIFont) -> CTFont {
14
+ font as CTFont
15
+ }
16
+
17
+ func prepare(
18
+ document: ViewerDocument,
19
+ key: ProseLayoutKey,
20
+ widthPoints: CGFloat,
21
+ displayScale: CGFloat,
22
+ semanticGenerationIdentity: String? = nil
23
+ ) throws -> PreparedProseLayout {
24
+ // This context is deliberately passed separately from the layout key's
25
+ // revision-sensitive generation identity. A replacement layout for an
26
+ // attachment/font/width revision must not reopen a missing-font warning.
27
+ let warningSemanticGeneration = semanticGenerationIdentity ?? key.semanticGenerationIdentity
28
+ guard let widthPixels = ProseLayoutMetrics.widthPixels(widthPoints: widthPoints, scale: displayScale) else {
29
+ return .error(key: key, width: 0, error: .hostContract(message: "A finite positive width is required for prose measurement."))
30
+ }
31
+ let canonicalWidth = ProseLayoutMetrics.canonicalWidth(widthPixels: widthPixels, scale: displayScale)
32
+ if document.isEmpty {
33
+ return PreparedProseLayout(key: key, size: CGSize(width: canonicalWidth, height: 0), blocks: [], retainedBytes: document.retainedBytes)
34
+ }
35
+
36
+ let theme = document.preparedTheme ?? PreparedProseTheme.resolve(themeJSON: nil)
37
+ let highlightingRequest = theme.codeHighlighting.map { configuration in
38
+ PreparedViewerHighlightingRequest(configuration: configuration, generation: key.semanticGenerationIdentity, blocks: document.blocks.enumerated().compactMap { index, block in
39
+ guard EditorStyleSheet.element(block.nodeType) == "codeBlock" else { return nil }
40
+ let text = block.inlines.compactMap { inline -> String? in
41
+ if case let .text(text, _) = inline { return text }
42
+ return nil
43
+ }.joined()
44
+ return NativeCodeHighlightBlock(start: index, text: text, language: block.language)
45
+ })
46
+ }
47
+ let highlighting = highlightingRequest.flatMap { PreparedViewerHighlightingStore.result(for: $0.generation) }
48
+ var cursorY = theme.contentInsets.top
49
+ var blocks: [PreparedProseBlock] = []
50
+ var containerBounds: [Int: CGRect] = [:]
51
+ var containerStyles: [Int: (EditorStyleBox, Int)] = [:]
52
+ var interactions: [PreparedProseInteraction] = []
53
+ var accessibilityNodes: [PreparedProseAccessibilityNode] = []
54
+ var imageAttachments: [ViewerImageAttachment] = []
55
+ var retainedBytes = document.retainedBytes
56
+ var listMarkersByIdentity: [Int: PreparedListMarker] = [:]
57
+ for block in document.blocks {
58
+ guard let boundary = block.listItemBoundary,
59
+ let context = block.listContext,
60
+ listMarkersByIdentity[boundary.identity] == nil
61
+ else { continue }
62
+ let markerNestingDepth = block.listItemAncestors.firstIndex { ancestor in
63
+ ancestor.identity == boundary.identity
64
+ } ?? 0
65
+ listMarkersByIdentity[boundary.identity] = makeListMarker(
66
+ context,
67
+ nestingDepth: markerNestingDepth,
68
+ paint: theme.paint(for: block),
69
+ theme: theme
70
+ )
71
+ }
72
+ for (index, block) in document.blocks.enumerated() {
73
+ let listMarker = block.listItemBoundary.flatMap { listMarkersByIdentity[$0.identity] }
74
+ let nextAncestorIdentities = Set(
75
+ document.blocks.indices.contains(index + 1)
76
+ ? listItemAncestors(document.blocks[index + 1]).map(\.identity)
77
+ : []
78
+ )
79
+ let disappearingListItemIdentities = Set(
80
+ listItemAncestors(block)
81
+ .filter { !nextAncestorIdentities.contains($0.identity) }
82
+ .map(\.identity)
83
+ )
84
+ let priorIds = Set(index > 0 ? document.blocks[index - 1].styleAncestors.map(\.identity) : [])
85
+ let followingIds = Set(document.blocks.indices.contains(index + 1) ? document.blocks[index + 1].styleAncestors.map(\.identity) : [])
86
+ let opening = block.styleAncestors.filter { !priorIds.contains($0.identity) }
87
+ let closing = block.styleAncestors.filter { !followingIds.contains($0.identity) }
88
+ if let sheet = theme.styleSheet, index > 0 {
89
+ let previous = document.blocks[index - 1]
90
+ let shared = zip(previous.styleAncestors, block.styleAncestors).prefix { $0 == $1 }.count
91
+ let previousSibling = previous.styleAncestors.dropFirst(shared).first?.nodeType ?? previous.nodeType
92
+ let nextSibling = block.styleAncestors.dropFirst(shared).first?.nodeType ?? block.nodeType
93
+ let previousMargin = sheet.box(previousSibling).margin.bottom
94
+ let nextMargin = sheet.box(nextSibling).margin.top
95
+ cursorY -= previousMargin + nextMargin - EditorStyleSheet.collapsedMargin(previousMargin, nextMargin)
96
+ }
97
+ let omitBottomMargin = block.nodeType == "paragraph"
98
+ && block.styleAncestors.last.map { $0.nodeType == "blockquote" && closing.contains($0) } == true
99
+ let top = opening.reduce(CGFloat.zero) { $0 + (theme.styleSheet?.box($1.nodeType).outerInsets.top ?? 0) }
100
+ let bottom = closing.reduce(CGFloat.zero) { $0 + (theme.styleSheet?.box($1.nodeType).outerInsets.bottom ?? 0) }
101
+ cursorY += top
102
+ let prepared = prepareBlock(
103
+ block,
104
+ highlighting: highlighting?.ranges[index] ?? [],
105
+ attachmentOrdinal: imageAttachments.count,
106
+ listMarker: listMarker,
107
+ theme: theme,
108
+ width: canonicalWidth,
109
+ cursorY: cursorY,
110
+ omitBottomMargin: omitBottomMargin,
111
+ disappearingListItemIdentities: disappearingListItemIdentities,
112
+ displayScale: displayScale,
113
+ warningSemanticGeneration: warningSemanticGeneration
114
+ )
115
+ blocks.append(prepared.block)
116
+ let interactionIndexOffset = interactions.count
117
+ accessibilityNodes.append(contentsOf: prepared.accessibilityNodes.map { node in
118
+ PreparedProseAccessibilityNode(
119
+ interactionIndex: node.interactionIndex.map { interactionIndexOffset + $0 },
120
+ role: node.role,
121
+ label: node.label,
122
+ rects: node.rects
123
+ )
124
+ })
125
+ interactions.append(contentsOf: prepared.interactions)
126
+ if let attachment = prepared.attachment { imageAttachments.append(attachment) }
127
+ if let sheet = theme.styleSheet {
128
+ var outerLeft: CGFloat = 0
129
+ var outerRight: CGFloat = 0
130
+ for (depth, ancestor) in block.styleAncestors.enumerated() {
131
+ let box = sheet.box(ancestor.nodeType)
132
+ let remaining = block.styleAncestors.dropFirst(depth + 1)
133
+ let innerTop = remaining.reduce(CGFloat.zero) { $0 + (opening.contains($1) ? sheet.box($1.nodeType).outerInsets.top : 0) }
134
+ let innerBottom = remaining.reduce(CGFloat.zero) { $0 + (closing.contains($1) ? sheet.box($1.nodeType).outerInsets.bottom : 0) }
135
+ let y = cursorY - innerTop - (opening.contains(ancestor) ? box.inset.top : 0)
136
+ let end = prepared.nextY + innerBottom + (closing.contains(ancestor) ? box.inset.bottom : 0)
137
+ let rect = CGRect(
138
+ x: theme.contentInsets.left + outerLeft + box.margin.left,
139
+ y: y,
140
+ width: max(1, canonicalWidth - theme.contentInsets.left - theme.contentInsets.right - outerLeft - outerRight - box.margin.left - box.margin.right),
141
+ height: max(0, end - y)
142
+ )
143
+ containerBounds[ancestor.identity] = containerBounds[ancestor.identity].map { $0.union(rect) } ?? rect
144
+ containerStyles[ancestor.identity] = (box, depth)
145
+ outerLeft += box.outerInsets.left
146
+ outerRight += box.outerInsets.right
147
+ }
148
+ }
149
+ cursorY = prepared.nextY + bottom
150
+ retainedBytes += prepared.retainedBytes
151
+ }
152
+ let renderedBottom = blocks.map(\.bounds.maxY).max() ?? cursorY
153
+ cursorY = (theme.styleSheet == nil ? renderedBottom : max(cursorY, renderedBottom)) + theme.contentInsets.bottom
154
+ var decorations = containerBounds.keys.sorted { (containerStyles[$0]?.1 ?? 0) < (containerStyles[$1]?.1 ?? 0) }.compactMap { identity -> PreparedProseFragment? in
155
+ guard let bounds = containerBounds[identity], let style = containerStyles[identity]?.0 else { return nil }
156
+ return PreparedProseFragment(kind: .background, bounds: bounds, styleBox: style)
157
+ }
158
+ if let sheet = theme.styleSheet {
159
+ decorations.insert(PreparedProseFragment(kind: .background, bounds: CGRect(x: 0, y: 0, width: canonicalWidth, height: cursorY), styleBox: sheet.box("content")), at: 0)
160
+ }
161
+ retainedBytes += decorations.count * 512 + (highlightingRequest?.retainedBytes ?? 0) + (highlighting?.retainedBytes ?? 0)
162
+ let pixelHeight = ceil(cursorY * displayScale)
163
+ retainedBytes += interactions.reduce(0) { $0 + $1.estimatedRetainedBytes }
164
+ + accessibilityNodes.reduce(0) { $0 + $1.estimatedRetainedBytes }
165
+ // Mounted image-publication sidecars are runtime surface ownership,
166
+ // not immutable artifact/cache ownership; account them at the host.
167
+ return PreparedProseLayout(
168
+ key: key,
169
+ size: CGSize(width: canonicalWidth, height: pixelHeight / displayScale),
170
+ blocks: blocks,
171
+ interactions: interactions,
172
+ accessibilityNodes: accessibilityNodes,
173
+ imageAttachments: imageAttachments,
174
+ retainedBytes: retainedBytes,
175
+ decorations: decorations,
176
+ highlightingRequest: highlightingRequest,
177
+ highlightingResolved: highlighting != nil
178
+ )
179
+ }
180
+
181
+ private func listItemAncestors(_ block: ViewerBlock) -> [ViewerListItemAncestor] {
182
+ if !block.listItemAncestors.isEmpty {
183
+ return block.listItemAncestors
184
+ }
185
+ guard let boundary = block.listItemBoundary,
186
+ let context = block.listContext
187
+ else { return [] }
188
+ return [ViewerListItemAncestor(identity: boundary.identity, context: context)]
189
+ }
190
+
191
+ private struct BlockPreparation {
192
+ let block: PreparedProseBlock
193
+ let interactions: [PreparedProseInteraction]
194
+ let accessibilityNodes: [PreparedProseAccessibilityNode]
195
+ let attachment: ViewerImageAttachment?
196
+ let nextY: CGFloat
197
+ let retainedBytes: Int
198
+ }
199
+
200
+ private func prepareBlock(
201
+ _ block: ViewerBlock,
202
+ highlighting: [NativeCodeHighlightRange],
203
+ attachmentOrdinal: Int,
204
+ listMarker: PreparedListMarker?,
205
+ theme: PreparedProseTheme,
206
+ width: CGFloat,
207
+ cursorY: CGFloat,
208
+ omitBottomMargin: Bool,
209
+ disappearingListItemIdentities: Set<Int>,
210
+ displayScale: CGFloat,
211
+ warningSemanticGeneration: String
212
+ ) -> BlockPreparation {
213
+ let sheet = theme.styleSheet
214
+ let box = sheet?.box(block.nodeType) ?? EditorStyleBox()
215
+ let ancestors = block.styleAncestors.reduce(UIEdgeInsets.zero) { $0.adding(sheet?.box($1.nodeType).outerInsets ?? .zero) }
216
+ let cursorY = cursorY + box.margin.top
217
+ let contentX = theme.contentInsets.left + ancestors.left + box.margin.left
218
+ let contentWidth = max(1, width - theme.contentInsets.left - theme.contentInsets.right - ancestors.left - ancestors.right - box.margin.left - box.margin.right)
219
+ let paint = theme.paint(for: block)
220
+ let listDepth = block.listContext == nil
221
+ ? 0
222
+ : (block.listItemBoundary.map { Int($0.nestingDepth) } ?? max(0, Int(block.depth) - 1))
223
+ let fallbackMarkerNestingDepth = max(0, block.listItemAncestors.count - 1)
224
+ let measuredListMarker = listMarker ?? block.listContext.map {
225
+ makeListMarker($0, nestingDepth: fallbackMarkerNestingDepth, paint: paint, theme: theme)
226
+ }
227
+ let marker = block.listItemBoundary.map { $0.isFirstRenderableLeaf ? measuredListMarker : nil } ?? measuredListMarker
228
+ // The marker gutter is an independently measured column. In particular,
229
+ // baseIndentMultiplier == 0 must not permit text to overlap a scaled
230
+ // ordered marker or task box. `measuredListMarker` stays item-scoped,
231
+ // which keeps paragraph/code/atom descendants aligned.
232
+ let listName = block.listContext.map { $0.kind == "task" ? "taskList" : $0.ordered ? "orderedList" : "bulletList" } ?? "bulletList"
233
+ let listValues = sheet?[listName] ?? [:]
234
+ let listIndent = EditorTheme.cgFloat(listValues["indent"]) ?? theme.listIndent
235
+ let baseMultiplier = EditorTheme.cgFloat(listValues["baseIndentMultiplier"]) ?? theme.listBaseIndentMultiplier
236
+ let listBaseIndent = block.listContext == nil ? 0 : max(0, listIndent * baseMultiplier)
237
+ let nestedListIndent = block.listContext == nil ? 0 : max(0, listIndent * CGFloat(listDepth))
238
+ let checkbox = block.listContext.flatMap { $0.kind == "task" ? sheet?.checkbox(checked: $0.checked) : nil }
239
+ let markerGap = checkbox?.number("gap", fallback: 8) ?? theme.listMarkerGap
240
+ let markerGutter = measuredListMarker.map { max(markerGap, $0.width + markerGap) } ?? 0
241
+ let listInset = listBaseIndent + nestedListIndent + markerGutter
242
+ let quoteInset = block.inBlockquote ? theme.quoteBorderWidth + theme.quoteMarkerGap + theme.quoteIndent : 0
243
+ let codeInset = block.nodeType == "codeBlock" ? theme.codePaddingHorizontal : 0
244
+ let textX = contentX + listInset + quoteInset + codeInset + box.inset.left
245
+ let itemSpacing: CGFloat
246
+ if sheet != nil {
247
+ itemSpacing = omitBottomMargin ? 0 : box.margin.bottom
248
+ } else if block.listContext == nil {
249
+ itemSpacing = paint.spacingAfter
250
+ } else {
251
+ let boundarySpacing = listItemAncestors(block).reduce(CGFloat.zero) { spacing, ancestor in
252
+ if disappearingListItemIdentities.contains(ancestor.identity) {
253
+ return spacing + (ancestor.context.isLast ? theme.listSpacingAfter : theme.listItemSpacing)
254
+ }
255
+ if ancestor.identity == block.listItemBoundary?.identity,
256
+ block.listItemBoundary?.isFinalRenderableLeaf == true {
257
+ return spacing + theme.listItemSpacing
258
+ }
259
+ return spacing
260
+ }
261
+ itemSpacing = boundarySpacing
262
+ }
263
+ if block.isBlockAtom, let atoms = theme.viewerAtoms,
264
+ atoms.nodeTypes.contains(block.nodeType),
265
+ case let .atom(nodeType, docPos, attrsJSON, _)? = block.inlines.first {
266
+ let slotWidth = max(1, contentWidth - listInset - quoteInset)
267
+ let height = atoms.height(nodeType: nodeType, docPos: docPos, width: slotWidth)
268
+ let bounds = CGRect(x: textX, y: cursorY, width: slotWidth, height: height)
269
+ var fragments: [PreparedProseFragment] = []
270
+ if block.inBlockquote {
271
+ fragments.append(.init(
272
+ kind: .border,
273
+ bounds: CGRect(
274
+ x: contentX,
275
+ y: cursorY,
276
+ width: theme.quoteBorderWidth,
277
+ height: height
278
+ ),
279
+ color: theme.quoteBorderColor.cgColor,
280
+ strokeWidth: theme.quoteBorderWidth
281
+ ))
282
+ }
283
+ if let marker {
284
+ let markerX = textX - markerGutter
285
+ let markerTop = cursorY + max(0, (height - marker.ascent - marker.descent) / 2)
286
+ let markerBounds = CGRect(
287
+ x: markerX,
288
+ y: markerTop,
289
+ width: marker.width,
290
+ height: marker.ascent + marker.descent
291
+ )
292
+ fragments.append(.init(
293
+ kind: .marker,
294
+ line: marker.line,
295
+ origin: CGPoint(x: markerX, y: markerTop + marker.ascent),
296
+ bounds: markerBounds,
297
+ color: theme.listMarkerColor.cgColor,
298
+ label: marker.label,
299
+ checked: marker.checked,
300
+ styleBox: checkbox
301
+ ))
302
+ }
303
+ let blockBounds = fragments.reduce(bounds) { $0.union($1.bounds) }
304
+ let prepared = PreparedProseBlock(
305
+ fragments: fragments,
306
+ bounds: blockBounds,
307
+ atomSlot: PreparedProseAtomSlot(nodeType: nodeType, docPos: docPos, attrsJSON: attrsJSON, bounds: bounds)
308
+ )
309
+ return BlockPreparation(block: prepared, interactions: [], accessibilityNodes: [], attachment: nil, nextY: blockBounds.maxY + itemSpacing, retainedBytes: prepared.estimatedRetainedBytes)
310
+ }
311
+ if block.nodeType == "image", let image = ViewerImageAttachment.sourceAndDeclaredSize(in: block) {
312
+ let availableImageWidth = max(1, contentWidth - listInset - quoteInset - box.inset.left - box.inset.right)
313
+ let imageWidth = sheet == nil ? availableImageWidth : min(availableImageWidth, image.declaredSize?.width ?? availableImageWidth)
314
+ let provisionalHeight = max(44, min(240, imageWidth * 0.56))
315
+ let declared = image.declaredSize
316
+ let resolvedSize = declared ?? ViewerImageIntrinsicStore.shared.size(for: image.id)
317
+ let height = resolvedSize.map { imageWidth * $0.height / max(1, $0.width) } ?? provisionalHeight
318
+ let bounds = CGRect(x: textX - box.inset.left, y: cursorY, width: imageWidth + box.inset.left + box.inset.right, height: height + box.inset.top + box.inset.bottom)
319
+ let attachment = ViewerImageAttachment(ordinal: attachmentOrdinal, id: image.id, source: image.source, bounds: bounds, declaredSize: declared)
320
+ let fragments = [PreparedProseFragment(kind: .image, bounds: bounds, color: UIColor.systemGray5.cgColor, styleBox: sheet == nil ? nil : box)]
321
+ let prepared = PreparedProseBlock(fragments: fragments, bounds: bounds)
322
+ let imageLabel = block.inlines.compactMap { inline -> String? in
323
+ guard case let .atom("image", _, attrsJSON, _) = inline else { return nil }
324
+ let alt = jsonDictionary(attrsJSON)["alt"] as? String
325
+ return alt?.trimmingCharacters(in: .whitespacesAndNewlines)
326
+ }.first
327
+ let accessibleImageLabel = imageLabel.flatMap { $0.isEmpty ? nil : $0 } ?? "Image"
328
+ let node = PreparedProseAccessibilityNode(
329
+ interactionIndex: nil,
330
+ role: .image,
331
+ label: accessibleImageLabel,
332
+ bounds: bounds
333
+ )
334
+ return BlockPreparation(block: prepared, interactions: [], accessibilityNodes: [node], attachment: attachment, nextY: bounds.maxY + itemSpacing, retainedBytes: prepared.estimatedRetainedBytes + 192)
335
+ }
336
+ if block.nodeType == "horizontalRule" || block.nodeType == "horizontal_rule" {
337
+ let ruleX = contentX + listInset + quoteInset + box.inset.left
338
+ let ruleWidth = max(1, contentWidth - listInset - quoteInset - box.inset.left - box.inset.right)
339
+ let y = cursorY + theme.ruleMargin + box.inset.top
340
+ let rule = CGRect(x: ruleX, y: y, width: ruleWidth, height: theme.ruleThickness)
341
+ var fragments: [PreparedProseFragment] = [.init(kind: .rule, bounds: rule, color: theme.ruleColor.cgColor, strokeWidth: theme.ruleThickness)]
342
+ let totalEnd = y + theme.ruleThickness + theme.ruleMargin + box.inset.bottom
343
+ if sheet != nil {
344
+ fragments = [.init(kind: .background, bounds: CGRect(x: contentX, y: cursorY, width: contentWidth, height: totalEnd - cursorY), styleBox: box)]
345
+ }
346
+ if block.inBlockquote {
347
+ fragments.append(.init(kind: .border, bounds: CGRect(x: contentX, y: cursorY, width: theme.quoteBorderWidth, height: totalEnd - cursorY), color: theme.quoteBorderColor.cgColor, strokeWidth: theme.quoteBorderWidth))
348
+ }
349
+ if let marker {
350
+ let markerX = textX - markerGutter
351
+ let markerHeight = marker.ascent + marker.descent
352
+ let markerTop = cursorY + (totalEnd - cursorY - markerHeight) / 2
353
+ let markerBaseline = markerTop + marker.ascent
354
+ let markerBounds = CGRect(x: markerX, y: markerTop, width: marker.width, height: markerHeight)
355
+ fragments.append(.init(kind: .marker, line: marker.line, origin: CGPoint(x: markerX, y: markerBaseline), bounds: markerBounds, color: theme.listMarkerColor.cgColor, label: marker.label, checked: marker.checked, styleBox: checkbox))
356
+ }
357
+ let seedBounds = CGRect(x: contentX, y: cursorY, width: contentWidth, height: totalEnd - cursorY)
358
+ let bounds = fragments.reduce(seedBounds) { $0.union($1.bounds) }
359
+ let prepared = PreparedProseBlock(
360
+ fragments: fragments,
361
+ bounds: bounds
362
+ )
363
+ return BlockPreparation(
364
+ block: prepared,
365
+ interactions: [],
366
+ accessibilityNodes: [PreparedProseAccessibilityNode(
367
+ interactionIndex: nil,
368
+ role: .separator,
369
+ label: "Separator",
370
+ bounds: bounds
371
+ )],
372
+ attachment: nil,
373
+ nextY: totalEnd + itemSpacing,
374
+ retainedBytes: prepared.estimatedRetainedBytes
375
+ )
376
+ }
377
+
378
+ let availableWidth = max(1, contentWidth - listInset - quoteInset - codeInset * 2 - box.inset.left - box.inset.right)
379
+ let attributed = makeAttributedString(block.inlines, paint: paint, theme: theme, warningSemanticGeneration: warningSemanticGeneration)
380
+ let highlighted = NSMutableAttributedString(attributedString: attributed.string)
381
+ NativeCodeHighlightPresentation.apply(highlighting, to: highlighted)
382
+ let typesetter = CTTypesetterCreateWithAttributedString(highlighted)
383
+ var location = 0
384
+ var fragments: [PreparedProseFragment] = []
385
+ var interactionRects: [[CGRect]] = Array(repeating: [], count: attributed.semanticRanges.count)
386
+ var accessibilityRects: [[CGRect]] = Array(repeating: [], count: attributed.accessibilityRanges.count)
387
+ let semanticGeometryRanges = attributed.semanticRanges.enumerated().map {
388
+ (index: $0.offset, range: $0.element.range)
389
+ }
390
+ let accessibilityGeometryRanges = attributed.accessibilityRanges.enumerated().compactMap { index, range -> (index: Int, range: NSRange)? in
391
+ guard range.role == .text else { return nil }
392
+ return (index, range.range)
393
+ }
394
+ var semanticGeometryCursor = 0
395
+ var accessibilityGeometryCursor = 0
396
+ let codeTopInset = (block.nodeType == "codeBlock" ? theme.codePaddingVertical : 0) + box.inset.top
397
+ let firstLineHeight = max(paint.font.lineHeight, paint.lineHeight ?? 0)
398
+ let markerTopProtection = marker.map {
399
+ max(0, ($0.ascent + $0.descent - firstLineHeight) / 2 - codeTopInset)
400
+ } ?? 0
401
+ var textTop = cursorY + codeTopInset + (cursorY == theme.contentInsets.top ? markerTopProtection : 0)
402
+ var firstLineBounds: CGRect?
403
+ while location < attributed.string.length {
404
+ let suggested = CTTypesetterSuggestLineBreak(typesetter, location, availableWidth)
405
+ let count = max(1, suggested)
406
+ let shapedLine = CTTypesetterCreateLine(typesetter, CFRange(location: location, length: count))
407
+ let shouldJustify = paint.textValues["textAlign"] as? String == "justify" && location + count < attributed.string.length && (attributed.string.string as NSString).character(at: location + count - 1) != 10
408
+ let line = shouldJustify ? CTLineCreateJustifiedLine(shapedLine, 1, availableWidth) ?? shapedLine : shapedLine
409
+ var ascent: CGFloat = 0
410
+ var descent: CGFloat = 0
411
+ var leading: CGFloat = 0
412
+ let lineWidth = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading))
413
+ let naturalHeight = ascent + descent + leading
414
+ var requestedHeight = paint.lineHeight ?? 0
415
+ attributed.string.enumerateAttribute(editorInlineLineHeightAttribute, in: NSRange(location: location, length: count)) { value, _, _ in
416
+ requestedHeight = max(requestedHeight, EditorTheme.cgFloat(value) ?? 0)
417
+ }
418
+ let lineHeight = max(naturalHeight, requestedHeight)
419
+ let baseline = textTop + (lineHeight - naturalHeight) / 2 + ascent
420
+ let alignment = paint.textValues["textAlign"] as? String
421
+ let lineTextX = textX + (alignment == "center" ? max(0, availableWidth - lineWidth) / 2 : alignment == "right" ? max(0, availableWidth - lineWidth) : 0)
422
+ let lineBounds = CGRect(x: lineTextX, y: textTop, width: min(availableWidth, max(0, lineWidth)), height: lineHeight)
423
+ fragments.append(contentsOf: inlineBackgroundFragments(for: line, bounds: lineBounds))
424
+ let lineRange = NSRange(location: location, length: count)
425
+ fragments.append(.init(kind: .text, line: line, origin: CGPoint(x: lineTextX, y: baseline), bounds: lineBounds))
426
+ fragments.append(contentsOf: strikeFragments(
427
+ for: line,
428
+ lineOrigin: CGPoint(x: lineTextX, y: baseline),
429
+ displayScale: displayScale
430
+ ))
431
+ appendShapedRects(
432
+ ranges: semanticGeometryRanges,
433
+ line: line,
434
+ lineRange: lineRange,
435
+ lineBounds: lineBounds,
436
+ textX: lineTextX,
437
+ displayScale: displayScale,
438
+ rangeCursor: &semanticGeometryCursor,
439
+ to: &interactionRects
440
+ )
441
+ appendShapedRects(
442
+ ranges: accessibilityGeometryRanges,
443
+ line: line,
444
+ lineRange: lineRange,
445
+ lineBounds: lineBounds,
446
+ textX: lineTextX,
447
+ displayScale: displayScale,
448
+ rangeCursor: &accessibilityGeometryCursor,
449
+ to: &accessibilityRects
450
+ )
451
+ if firstLineBounds == nil { firstLineBounds = lineBounds }
452
+ for atom in attributed.atoms where NSIntersectionRange(atom.range, lineRange).length > 0 {
453
+ let offset = CGFloat(CTLineGetOffsetForStringIndex(line, atom.range.location, nil))
454
+ let atomBounds = CGRect(
455
+ x: lineTextX + offset,
456
+ y: baseline - atom.metrics.ascent,
457
+ width: atom.metrics.width,
458
+ height: atom.metrics.ascent + atom.metrics.descent
459
+ )
460
+ fragments.append(contentsOf: strikeFragments(for: atom.line, lineOrigin: CGPoint(x: atomBounds.minX + atom.appearance.padding.left, y: baseline), displayScale: displayScale))
461
+ fragments.append(
462
+ .init(
463
+ kind: .atom,
464
+ line: atom.line,
465
+ origin: CGPoint(x: atomBounds.minX + atom.appearance.padding.left, y: baseline),
466
+ bounds: atomBounds,
467
+ color: atom.appearance.background.cgColor,
468
+ borderColor: atom.appearance.borderColor?.cgColor,
469
+ cornerRadius: atom.appearance.radius,
470
+ strokeWidth: atom.appearance.borderWidth,
471
+ padding: atom.appearance.padding,
472
+ label: atom.label,
473
+ styleBox: atom.appearance.styleBox
474
+ )
475
+ )
476
+ }
477
+ location += count
478
+ textTop += lineHeight
479
+ }
480
+ if fragments.isEmpty {
481
+ let fallbackHeight = paint.lineHeight ?? paint.font.lineHeight
482
+ let line = CTLineCreateWithAttributedString(NSAttributedString(string: "\u{200B}", attributes: baseAttributes(paint)))
483
+ let alignment = paint.textValues["textAlign"] as? String
484
+ let lineTextX = textX + (alignment == "center" ? max(0, availableWidth) / 2 : alignment == "right" ? max(0, availableWidth) : 0)
485
+ let lineBounds = CGRect(x: lineTextX, y: textTop, width: 0, height: fallbackHeight)
486
+ fragments.append(.init(kind: .text, line: line, origin: CGPoint(x: textX, y: textTop + paint.font.ascender), bounds: lineBounds))
487
+ firstLineBounds = lineBounds
488
+ textTop += fallbackHeight
489
+ }
490
+ let textEnd = textTop
491
+ let totalEnd = textEnd + (block.nodeType == "codeBlock" ? theme.codePaddingVertical : 0) + box.inset.bottom
492
+ let blockRect = CGRect(x: contentX, y: cursorY, width: contentWidth, height: max(0, totalEnd - cursorY))
493
+ if sheet != nil {
494
+ fragments.insert(.init(kind: .background, bounds: blockRect, styleBox: box), at: 0)
495
+ } else if block.nodeType == "codeBlock" {
496
+ fragments.insert(.init(kind: .background, bounds: blockRect, color: theme.codeBackground.cgColor, cornerRadius: theme.codeRadius), at: 0)
497
+ }
498
+ if block.inBlockquote {
499
+ let border = CGRect(x: contentX, y: cursorY, width: theme.quoteBorderWidth, height: max(0, totalEnd - cursorY))
500
+ fragments.append(.init(kind: .border, bounds: border, color: theme.quoteBorderColor.cgColor, strokeWidth: theme.quoteBorderWidth))
501
+ }
502
+ if let marker, let firstLineBounds {
503
+ let markerX = textX - markerGutter
504
+ let markerHeight = marker.ascent + marker.descent
505
+ let markerTop = firstLineBounds.midY - markerHeight / 2
506
+ let markerBaseline = markerTop + marker.ascent
507
+ let markerBounds = CGRect(
508
+ x: markerX,
509
+ y: markerTop,
510
+ width: marker.width,
511
+ height: markerHeight
512
+ )
513
+ fragments.append(.init(kind: .marker, line: marker.line, origin: CGPoint(x: markerX, y: markerBaseline), bounds: markerBounds, color: theme.listMarkerColor.cgColor, label: marker.label, checked: marker.checked, styleBox: checkbox))
514
+ }
515
+ let seedBounds = CGRect(x: contentX, y: cursorY, width: contentWidth, height: max(0, totalEnd - cursorY))
516
+ let bounds = fragments.reduce(seedBounds) { $0.union($1.bounds) }
517
+ let prepared = PreparedProseBlock(fragments: fragments, bounds: bounds)
518
+ var interactions: [PreparedProseInteraction] = []
519
+ var interactionIndexBySemanticIndex: [Int: Int] = [:]
520
+ for (semanticIndex, semantic) in attributed.semanticRanges.enumerated() {
521
+ let rects = interactionRects[semanticIndex]
522
+ guard !rects.isEmpty else { continue }
523
+ let interaction: PreparedProseInteraction
524
+ switch semantic {
525
+ case let .link(_, href, text):
526
+ interaction = PreparedProseInteraction(kind: .link, rects: rects, href: href, visibleText: text, docPos: nil, label: text, attrsJSON: nil)
527
+ case let .mention(_, docPos, label, attrsJSON):
528
+ interaction = PreparedProseInteraction(kind: .mention, rects: rects, href: nil, visibleText: label, docPos: docPos, label: label, attrsJSON: attrsJSON)
529
+ }
530
+ interactionIndexBySemanticIndex[semanticIndex] = interactions.count
531
+ interactions.append(interaction)
532
+ }
533
+ var markerPending = block.listItemBoundary?.isFirstRenderableLeaf == true
534
+ ? block.listContext.map { context in
535
+ context.kind == "task" ? (context.checked ? "Checked" : "Unchecked") : "Item"
536
+ }
537
+ : nil
538
+ let accessibilityNodes = attributed.accessibilityRanges.enumerated().compactMap { index, range -> PreparedProseAccessibilityNode? in
539
+ let label = range.label.trimmingCharacters(in: .whitespacesAndNewlines)
540
+ guard !label.isEmpty else { return nil }
541
+ let accessibleLabel: String
542
+ if let marker = markerPending {
543
+ markerPending = nil
544
+ accessibleLabel = "\(marker), \(label)"
545
+ } else {
546
+ accessibleLabel = label
547
+ }
548
+ let interactionIndex: Int?
549
+ let role: PreparedProseAccessibilityNode.Role
550
+ let rects: [CGRect]
551
+ switch range.role {
552
+ case .text:
553
+ interactionIndex = nil
554
+ role = block.nodeType == "heading" || theme.headings[block.nodeType] != nil ? .heading : .text
555
+ rects = accessibilityRects[index]
556
+ case let .link(semanticIndex):
557
+ interactionIndex = interactionIndexBySemanticIndex[semanticIndex]
558
+ role = interactionIndex == nil ? .text : .link
559
+ rects = interactionRects[semanticIndex]
560
+ case let .mention(semanticIndex):
561
+ interactionIndex = interactionIndexBySemanticIndex[semanticIndex]
562
+ role = interactionIndex == nil ? .text : .mention
563
+ rects = interactionRects[semanticIndex]
564
+ }
565
+ guard !rects.isEmpty else { return nil }
566
+ return PreparedProseAccessibilityNode(
567
+ interactionIndex: interactionIndex,
568
+ role: role,
569
+ label: accessibleLabel,
570
+ rects: rects
571
+ )
572
+ }
573
+ return BlockPreparation(
574
+ block: prepared,
575
+ interactions: interactions,
576
+ accessibilityNodes: accessibilityNodes,
577
+ attachment: nil,
578
+ nextY: totalEnd + itemSpacing,
579
+ retainedBytes: 256 + attributed.retainedBytes + prepared.estimatedRetainedBytes
580
+ )
581
+ }
582
+
583
+ private func appendShapedRects(
584
+ ranges: [(index: Int, range: NSRange)],
585
+ line: CTLine,
586
+ lineRange: NSRange,
587
+ lineBounds: CGRect,
588
+ textX: CGFloat,
589
+ displayScale: CGFloat,
590
+ rangeCursor: inout Int,
591
+ to rects: inout [[CGRect]]
592
+ ) {
593
+ guard ranges.indices.contains(rangeCursor) else { return }
594
+ while ranges.indices.contains(rangeCursor), ranges[rangeCursor].range.upperBound <= lineRange.location {
595
+ rangeCursor += 1
596
+ }
597
+ guard ranges.indices.contains(rangeCursor) else { return }
598
+ guard ranges[rangeCursor].range.location < lineRange.upperBound else { return }
599
+ let glyphRuns = CTLineGetGlyphRuns(line) as? [CTRun] ?? []
600
+ var index = rangeCursor
601
+ while ranges.indices.contains(index), ranges[index].range.location < lineRange.upperBound {
602
+ let geometryRange = ranges[index]
603
+ let range = geometryRange.range
604
+ var visualPieces: [(rect: CGRect, rightToLeft: Bool)] = []
605
+ for run in glyphRuns {
606
+ let stringRange = CTRunGetStringRange(run)
607
+ let runRange = NSRange(location: stringRange.location, length: stringRange.length)
608
+ let overlap = NSIntersectionRange(NSIntersectionRange(range, lineRange), runRange)
609
+ guard overlap.length > 0 else { continue }
610
+ let start = CGFloat(CTLineGetOffsetForStringIndex(line, overlap.location, nil))
611
+ let end = CGFloat(CTLineGetOffsetForStringIndex(line, overlap.location + overlap.length, nil))
612
+ visualPieces.append((
613
+ CGRect(
614
+ x: textX + min(start, end),
615
+ y: lineBounds.minY,
616
+ width: max(1 / displayScale, abs(end - start)),
617
+ height: lineBounds.height
618
+ ),
619
+ CTRunGetStatus(run).contains(.rightToLeft)
620
+ ))
621
+ }
622
+ var priorDirection: Bool?
623
+ for piece in visualPieces.sorted(by: { PreparedProseInteractionGeometry.visualOrder($0.rect, $1.rect) }) {
624
+ PreparedProseInteractionGeometry.appendSameLinePiece(
625
+ piece.rect,
626
+ to: &rects[geometryRange.index],
627
+ mayMergeWithPrior: priorDirection == piece.rightToLeft
628
+ )
629
+ priorDirection = piece.rightToLeft
630
+ }
631
+ index += 1
632
+ }
633
+ }
634
+
635
+ }
@@ -0,0 +1,4 @@
1
+ #import <React/RCTViewComponentView.h>
2
+
3
+ @interface PREPPreparedProseViewerComponentView : RCTViewComponentView
4
+ @end