@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,766 @@
1
+ import Foundation
2
+
3
+ /// Owns unmounted layouts and exact Yoga-to-Fabric handoffs. A lease shares
4
+ /// its immutable artifact with the LRU; lifecycle release retires any live
5
+ /// handoff before a later component can acquire it.
6
+ final class PreparedProseLayoutCache {
7
+ static let pixelGridRoundingSlackPixels = 1
8
+
9
+ private final class Preparation {
10
+ var result: Result<PreparedProseLayout, Error>?
11
+ }
12
+
13
+ private let condition = NSCondition()
14
+ private var completed: [ProseLayoutKey: PreparedProseLayout] = [:]
15
+ /// Accesses append a generation token instead of moving an existing array
16
+ /// element. Stale tokens are ignored at eviction time and periodically
17
+ /// compacted, so ordinary cache hits remain O(1) while eviction retains
18
+ /// exact LRU order.
19
+ private var accessOrder: [(key: ProseLayoutKey, generation: UInt64)] = []
20
+ private var accessGenerations: [ProseLayoutKey: UInt64] = [:]
21
+ private var accessOrderHead = 0
22
+ private var nextAccessGeneration: UInt64 = 0
23
+ private var inFlight: [ProseLayoutKey: Preparation] = [:]
24
+ /// Yoga measurement hands an immutable artifact to Fabric before there is
25
+ /// a component view. A pending handoff is therefore distinct from a
26
+ /// mounted owner: mount consumes it exactly once. Both are live exact
27
+ /// owners and remain retained until their lifecycle releases them.
28
+ private var pendingLeases: [FabricLeaseKey: PreparedProseLayout] = [:]
29
+ private var mountedLeases: [FabricLeaseKey: PreparedProseLayout] = [:]
30
+ private var directMounted: [String: PreparedProseLayout] = [:]
31
+ /// Secondary ownership indexes make mount/release lookup proportional to
32
+ /// the affected owners, not every retained artifact.
33
+ private var pendingLeaseKeysByOwner: [FabricLeaseOwner: Set<FabricLeaseKey>] = [:]
34
+ private var mountedLeaseKeysByOwner: [FabricLeaseOwner: Set<FabricLeaseKey>] = [:]
35
+ private var pendingLeaseKeysBySurface: [FabricSurfaceToken: Set<FabricLeaseKey>] = [:]
36
+ private var mountedLeaseKeysBySurface: [FabricSurfaceToken: Set<FabricLeaseKey>] = [:]
37
+ private var pendingLeaseKeysByLayout: [ProseLayoutKey: Set<FabricLeaseKey>] = [:]
38
+ private var mountedLeaseKeysByLayout: [ProseLayoutKey: Set<FabricLeaseKey>] = [:]
39
+ private var directOwnersByLayout: [ProseLayoutKey: Set<String>] = [:]
40
+
41
+ private struct LayoutOwnership {
42
+ let layout: PreparedProseLayout
43
+ var completedReferences = 0
44
+ var pendingReferences = 0
45
+ var mountedReferences = 0
46
+ var directReferences = 0
47
+ }
48
+
49
+ /// All retention totals are maintained on identity transitions. A layout
50
+ /// may be referenced by several roles, but is counted once in each metric
51
+ /// with the same exclusions as the former set-based calculations.
52
+ private var ownershipByIdentifier: [ObjectIdentifier: LayoutOwnership] = [:]
53
+ private var retainedBytes = 0
54
+ private var budgetedRetainedBytes = 0
55
+ private var unmountedRetainedBytes = 0
56
+ private var fabricLeaseRetainedBytes = 0
57
+ private var directMountedRetainedBytes = 0
58
+ private var oversizedLeaseCount = 0
59
+ private var benchmarkCensusKeys: Set<ProseLayoutKey>?
60
+ private var mountIndex: [ProseMountKey: ProseLayoutKey] = [:]
61
+ #if DEBUG
62
+ /// A live generation must publish an artifact once for its complete
63
+ /// semantic/physical-width/revision key. Eviction retires only unmounted
64
+ /// entries, so a mounted owner can never be republished accidentally.
65
+ private var publishedKeys: Set<ProseLayoutKey> = []
66
+ private var livePublicationCounts: [ProseLayoutKey: Int] = [:]
67
+ private var publicationRetirementCandidates: Set<ProseLayoutKey> = []
68
+ #endif
69
+ private let byteBudget: Int
70
+ init(byteBudget: Int = 32 * 1024 * 1024) {
71
+ self.byteBudget = byteBudget
72
+ }
73
+
74
+ func value(
75
+ for key: ProseLayoutKey,
76
+ fabricSurface: FabricSurfaceToken? = nil,
77
+ fabricLeaseHandle: UInt64? = nil,
78
+ shouldCreateFabricLease: (() -> Bool)? = nil,
79
+ build: () throws -> PreparedProseLayout
80
+ ) throws -> PreparedProseLayout {
81
+ precondition(fabricSurface == nil || fabricLeaseHandle != nil)
82
+ let lookupStarted = PreparedProseInstrumentation.now()
83
+ condition.lock()
84
+ benchmarkCensusKeys?.insert(key)
85
+ // Mounted ownership is the most authoritative exact-key cache entry:
86
+ // it survives unmounted-cache eviction and memory warnings. Consult it
87
+ // before completed entries, because creating a same-width pending lease
88
+ // would otherwise prune a different-width replacement already pending
89
+ // for this Fabric surface and generation.
90
+ if let layout = mountedLayoutLocked(
91
+ for: key,
92
+ fabricSurface: fabricSurface,
93
+ fabricLeaseHandle: fabricLeaseHandle
94
+ ) {
95
+ condition.unlock()
96
+ PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true)
97
+ return layout
98
+ }
99
+ if let layout = completed[key] {
100
+ touch(key)
101
+ if let fabricSurface, let fabricLeaseHandle, shouldCreateFabricLease?() ?? true {
102
+ createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
103
+ }
104
+ condition.unlock()
105
+ PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true)
106
+ return layout
107
+ }
108
+ // Immutable prepared layouts are global to their complete layout key,
109
+ // while Fabric ownership is intentionally surface-scoped. Any live
110
+ // owner can therefore satisfy another caller without preparing a
111
+ // duplicate artifact. Fabric receives its own exact-once pending
112
+ // lease; UIKit simply reuses the immutable value without inventing a
113
+ // Fabric owner.
114
+ if let layout = liveLayoutLocked(for: key) {
115
+ if let fabricSurface, let fabricLeaseHandle, shouldCreateFabricLease?() ?? true {
116
+ createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
117
+ }
118
+ condition.unlock()
119
+ PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true)
120
+ return layout
121
+ }
122
+ if let preparation = inFlight[key] {
123
+ while preparation.result == nil { condition.wait() }
124
+ let result = preparation.result!
125
+ if case let .success(layout) = result, let fabricSurface, let fabricLeaseHandle,
126
+ shouldCreateFabricLease?() ?? true {
127
+ createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
128
+ }
129
+ condition.unlock()
130
+ PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: true, waited: true)
131
+ return try result.get()
132
+ }
133
+ let preparation = Preparation()
134
+ inFlight[key] = preparation
135
+ condition.unlock()
136
+ PreparedProseInstrumentation.cacheLookup(lookupStarted, hit: false)
137
+
138
+ let result = Result(catching: build)
139
+
140
+ condition.lock()
141
+ if case let .success(layout) = result {
142
+ #if DEBUG
143
+ if !publishedKeys.insert(key).inserted {
144
+ PreparedProseInstrumentation.duplicatePublication()
145
+ preconditionFailure("Prepared prose layout published twice for a live semantic/width/revision key.")
146
+ }
147
+ // A build that is neither completed nor leased must retire before
148
+ // a later publication of the same key. Role insertion removes this
149
+ // candidate, so handoff transfers never transiently retire live
150
+ // publication state.
151
+ publicationRetirementCandidates.insert(key)
152
+ #endif
153
+ if layout.retainedBytes <= byteBudget {
154
+ insertCompletedLocked(layout, for: key)
155
+ }
156
+ if let fabricSurface, let fabricLeaseHandle, shouldCreateFabricLease?() ?? true {
157
+ createPendingLeaseLocked(layout, for: key, surface: fabricSurface, leaseHandle: fabricLeaseHandle)
158
+ } else {
159
+ enforceBudgetLocked()
160
+ }
161
+ }
162
+ preparation.result = result
163
+ inFlight.removeValue(forKey: key)
164
+ condition.broadcast()
165
+ condition.unlock()
166
+ return try result.get()
167
+ }
168
+
169
+ /// Used only by Fabric mount. It never builds, compiles, or prepares.
170
+ func acquireForFabricMount(
171
+ surface: FabricSurfaceToken,
172
+ generationIdentity: String,
173
+ widthPixels: Int,
174
+ displayScale: CGFloat,
175
+ leaseHandle: UInt64,
176
+ allowCompletedFallback: Bool = false,
177
+ shouldAcquire: () -> Bool = { true }
178
+ ) -> PreparedProseLayout? {
179
+ condition.lock()
180
+ defer { condition.unlock() }
181
+
182
+ let requestedMountKey = ProseMountKey(
183
+ generationIdentity: generationIdentity,
184
+ widthPixels: widthPixels,
185
+ displayScale: displayScale
186
+ )
187
+ // UIKit-only callers use the documented zero token because they have
188
+ // no Fabric owner. They retain the ordinary completed-cache lookup.
189
+ if surface.surfaceId == 0, surface.componentTag == 0,
190
+ let key = mountIndex[requestedMountKey], let layout = completed[key] {
191
+ touch(key)
192
+ return layout
193
+ }
194
+ let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
195
+ if let leaseKey = (pendingLeaseKeysByOwner[owner] ?? [])
196
+ .filter({
197
+ $0.layout.generationIdentity == requestedMountKey.generationIdentity
198
+ && $0.layout.displayScaleBits == requestedMountKey.displayScaleBits
199
+ && abs($0.layout.widthPixels - widthPixels) <= Self.pixelGridRoundingSlackPixels
200
+ })
201
+ .min(by: {
202
+ (abs($0.layout.widthPixels - widthPixels), $0.layout.widthPixels)
203
+ < (abs($1.layout.widthPixels - widthPixels), $1.layout.widthPixels)
204
+ }),
205
+ let layout = pendingLeases[leaseKey] {
206
+ guard shouldAcquire() else { return nil }
207
+ removePendingLeaseLocked(leaseKey)
208
+ for stale in mountedLeaseKeysByOwner[owner] ?? [] where stale != leaseKey {
209
+ removeMountedLeaseLocked(stale)
210
+ }
211
+ insertMountedLeaseLocked(layout, for: leaseKey)
212
+ enforceBudgetLocked()
213
+ return layout
214
+ }
215
+
216
+ guard allowCompletedFallback,
217
+ shouldAcquire(),
218
+ let key = mountIndex[requestedMountKey],
219
+ let layout = completed[key]
220
+ else { return nil }
221
+ let leaseKey = FabricLeaseKey(surface: surface, layout: key, leaseHandle: leaseHandle)
222
+ for stale in mountedLeaseKeysByOwner[owner] ?? [] where stale != leaseKey {
223
+ removeMountedLeaseLocked(stale)
224
+ }
225
+ insertMountedLeaseLocked(layout, for: leaseKey)
226
+ touch(key)
227
+ enforceBudgetLocked()
228
+ return layout
229
+ }
230
+
231
+ func releaseLease(
232
+ for surface: FabricSurfaceToken,
233
+ generationIdentity: String? = nil,
234
+ leaseHandle: UInt64? = nil
235
+ ) {
236
+ condition.lock()
237
+ let pending = matchingLeaseKeysLocked(
238
+ in: pendingLeaseKeysBySurface[surface] ?? [],
239
+ generationIdentity: generationIdentity,
240
+ leaseHandle: leaseHandle
241
+ )
242
+ let mounted = matchingLeaseKeysLocked(
243
+ in: mountedLeaseKeysBySurface[surface] ?? [],
244
+ generationIdentity: generationIdentity,
245
+ leaseHandle: leaseHandle
246
+ )
247
+ pending.forEach(removePendingLeaseLocked)
248
+ mounted.forEach(removeMountedLeaseLocked)
249
+ // Released mounted artifacts may still have completed entries. Those
250
+ // entries re-enter the disposable budget on this transition.
251
+ enforceBudgetLocked()
252
+ condition.unlock()
253
+ }
254
+
255
+ /// Commits a replacement generation for one state-family handle. Pending
256
+ /// G1 handoffs are stale immediately; mounted G1 remains visible until
257
+ /// G2's exact pending handoff is acquired by the normal mount path.
258
+ func activateFabricGeneration(
259
+ surface: FabricSurfaceToken,
260
+ generationIdentity: String,
261
+ leaseHandle: UInt64
262
+ ) {
263
+ condition.lock()
264
+ let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
265
+ for stale in pendingLeaseKeysByOwner[owner] ?? [] where stale.layout.generationIdentity != generationIdentity {
266
+ removePendingLeaseLocked(stale)
267
+ }
268
+ retireUnownedPublicationKeysLocked()
269
+ publishOwnerBytesLocked()
270
+ condition.unlock()
271
+ }
272
+
273
+ /// Removes only the unmounted handoff requested by a stale Fabric mount
274
+ /// callback. Mounted ownership is deliberately preserved: it may be the
275
+ /// currently displayed width while another width is pending.
276
+ ///
277
+ /// Returns whether another lease for the same surface/generation remains,
278
+ /// so the registry can retain its generation-scoped compiler pin.
279
+ func releasePendingLease(
280
+ for surface: FabricSurfaceToken,
281
+ generationIdentity: String,
282
+ widthPixels: Int,
283
+ displayScale: CGFloat,
284
+ leaseHandle: UInt64
285
+ ) -> Bool {
286
+ condition.lock()
287
+ defer { condition.unlock() }
288
+
289
+ let requested = ProseMountKey(
290
+ generationIdentity: generationIdentity,
291
+ widthPixels: widthPixels,
292
+ displayScale: displayScale
293
+ )
294
+ let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
295
+ if let leaseKey = pendingLeaseKeysByOwner[owner]?.first,
296
+ mountKey(for: leaseKey.layout) == requested {
297
+ removePendingLeaseLocked(leaseKey)
298
+ retireUnownedPublicationKeysLocked()
299
+ publishOwnerBytesLocked()
300
+ }
301
+ return (pendingLeaseKeysByOwner[owner] ?? []).contains { $0.layout.generationIdentity == generationIdentity }
302
+ || (mountedLeaseKeysByOwner[owner] ?? []).contains { $0.layout.generationIdentity == generationIdentity }
303
+ }
304
+
305
+ /// Surface shutdown must release mounted artifacts even after memory
306
+ /// pressure has already cleared registry compiler ownership. Return the
307
+ /// exact lease identities before mutating this bounded cache.
308
+ func fabricGenerations(for surface: FabricSurfaceToken) -> Set<FabricGenerationToken> {
309
+ condition.lock()
310
+ defer { condition.unlock() }
311
+ let leaseKeys = (pendingLeaseKeysBySurface[surface] ?? [])
312
+ .union(mountedLeaseKeysBySurface[surface] ?? [])
313
+ return Set(leaseKeys.map {
314
+ FabricGenerationToken(
315
+ surface: $0.surface,
316
+ generationIdentity: $0.layout.generationIdentity,
317
+ leaseHandle: $0.leaseHandle
318
+ )
319
+ })
320
+ }
321
+
322
+ func registerDirectMount(_ owner: String, layout: PreparedProseLayout) {
323
+ condition.lock()
324
+ insertDirectMountLocked(layout, for: owner)
325
+ // Replacing an owner can return its prior completed entry to the
326
+ // disposable budget even as the new artifact becomes direct-mounted.
327
+ enforceBudgetLocked()
328
+ condition.unlock()
329
+ }
330
+
331
+ func releaseDirectMount(_ owner: String) {
332
+ condition.lock()
333
+ removeDirectMountLocked(owner)
334
+ // A direct mount is excluded from the disposable-cache budget while
335
+ // it owns the artifact. Once released, its completed entry becomes
336
+ // unmounted again and must be evicted before telemetry observes it.
337
+ enforceBudgetLocked()
338
+ condition.unlock()
339
+ }
340
+
341
+ func removeAllUnmounted() {
342
+ condition.lock()
343
+ while let key = completed.keys.first { removeCompletedLocked(key) }
344
+ accessOrder.removeAll()
345
+ accessGenerations.removeAll()
346
+ accessOrderHead = 0
347
+ mountIndex.removeAll()
348
+ while let key = pendingLeases.keys.first { removePendingLeaseLocked(key) }
349
+ // Pending handoffs are unmounted owners, so memory pressure retires
350
+ // them with the completed cache. Fabric/direct mounted owners survive
351
+ // until their explicit release paths run. Registry clears compiled
352
+ // documents only after this unmounted cache step.
353
+ retireUnownedPublicationKeysLocked()
354
+ publishOwnerBytesLocked()
355
+ condition.unlock()
356
+ }
357
+
358
+ var countForTesting: Int {
359
+ condition.lock()
360
+ defer { condition.unlock() }
361
+ return completed.count
362
+ }
363
+
364
+ /// Benchmark reverse passes may not rebind a one-item or already-visible
365
+ /// cell. Seed only those passes with the exact live census from the
366
+ /// completed forward pass, then retain the normal end-of-pass liveness
367
+ /// intersection below.
368
+ func beginBenchmarkCensus(seeding keys: [ProseLayoutKey] = []) {
369
+ condition.lock()
370
+ benchmarkCensusKeys = Set(keys)
371
+ condition.unlock()
372
+ }
373
+
374
+ func endBenchmarkCensus() -> [ProseLayoutKey] {
375
+ condition.lock()
376
+ defer { condition.unlock() }
377
+ // A census records keys observed during a benchmark pass, but it is
378
+ // evidence of resident artifacts only. A lookup can fail, produce an
379
+ // oversized unowned artifact, or evict an earlier completed entry;
380
+ // none of those keys is resident when the pass ends. Pending and
381
+ // mounted Fabric leases plus direct UIKit mounts are genuine live
382
+ // owners even when the disposable completed LRU no longer has them.
383
+ let liveKeys = Set(completed.keys)
384
+ .union(pendingLeaseKeysByLayout.keys)
385
+ .union(mountedLeaseKeysByLayout.keys)
386
+ .union(directOwnersByLayout.keys)
387
+ let keys = benchmarkCensusKeys.map { Array($0.intersection(liveKeys)) } ?? []
388
+ benchmarkCensusKeys = nil
389
+ return keys
390
+ }
391
+
392
+ var retainedBytesForTesting: Int {
393
+ condition.lock()
394
+ defer { condition.unlock() }
395
+ return retainedBytes
396
+ }
397
+
398
+ var oversizedLeaseCountForTesting: Int {
399
+ condition.lock()
400
+ defer { condition.unlock() }
401
+ return oversizedLeaseCount
402
+ }
403
+
404
+ var pendingLeaseCountForTesting: Int {
405
+ condition.lock()
406
+ defer { condition.unlock() }
407
+ return pendingLeases.count
408
+ }
409
+
410
+ var mountedLeaseCountForTesting: Int {
411
+ condition.lock()
412
+ defer { condition.unlock() }
413
+ return mountedLeases.count
414
+ }
415
+
416
+ var leaseCountForTesting: Int {
417
+ condition.lock()
418
+ defer { condition.unlock() }
419
+ return pendingLeases.count + mountedLeases.count
420
+ }
421
+
422
+ var accessOrderTokenCountForTesting: Int {
423
+ condition.lock()
424
+ defer { condition.unlock() }
425
+ return accessOrder.count
426
+ }
427
+
428
+ private func createPendingLeaseLocked(
429
+ _ layout: PreparedProseLayout,
430
+ for key: ProseLayoutKey,
431
+ surface: FabricSurfaceToken,
432
+ leaseHandle: UInt64
433
+ ) {
434
+ let leaseKey = FabricLeaseKey(surface: surface, layout: key, leaseHandle: leaseHandle)
435
+ // A new measurement supersedes only unmounted handoffs for this
436
+ // Fabric surface and generation. Keep a mounted artifact alive until
437
+ // the replacement is actually acquired by a component view.
438
+ let owner = FabricLeaseOwner(surface: surface, leaseHandle: leaseHandle)
439
+ for stale in pendingLeaseKeysByOwner[owner] ?? [] where stale != leaseKey {
440
+ removePendingLeaseLocked(stale)
441
+ }
442
+
443
+ // Repeated Yoga measurements for an already mounted identity neither
444
+ // replace that mounted artifact nor manufacture another handoff.
445
+ guard mountedLeases[leaseKey] == nil else {
446
+ retireUnownedPublicationKeysLocked()
447
+ publishOwnerBytesLocked()
448
+ return
449
+ }
450
+ insertPendingLeaseLocked(layout, for: leaseKey)
451
+ enforceBudgetLocked()
452
+ }
453
+
454
+ private func removePendingLeaseLocked(_ key: FabricLeaseKey) {
455
+ guard let layout = pendingLeases.removeValue(forKey: key) else { return }
456
+ removeLeaseKey(key, from: &pendingLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
457
+ removeLeaseKey(key, from: &pendingLeaseKeysBySurface, key: key.surface)
458
+ removeLeaseKey(key, from: &pendingLeaseKeysByLayout, key: key.layout)
459
+ if layout.retainedBytes > byteBudget { oversizedLeaseCount -= 1 }
460
+ updateOwnershipLocked(layout, pendingDelta: -1, publicationKey: key.layout)
461
+ }
462
+
463
+ private func touch(_ key: ProseLayoutKey) {
464
+ guard completed[key] != nil else { return }
465
+ precondition(nextAccessGeneration < UInt64.max, "Prepared prose LRU generation overflowed.")
466
+ nextAccessGeneration += 1
467
+ accessGenerations[key] = nextAccessGeneration
468
+ accessOrder.append((key, nextAccessGeneration))
469
+ compactAccessOrderIfNeededLocked()
470
+ }
471
+
472
+ private func mountedLayoutLocked(
473
+ for key: ProseLayoutKey,
474
+ fabricSurface: FabricSurfaceToken?,
475
+ fabricLeaseHandle: UInt64?
476
+ ) -> PreparedProseLayout? {
477
+ if let fabricSurface {
478
+ // Fabric handoffs are owner-isolated: an artifact mounted by one
479
+ // surface must never satisfy another surface's measurement.
480
+ guard let fabricLeaseHandle else { return nil }
481
+ return mountedLeases[FabricLeaseKey(
482
+ surface: fabricSurface,
483
+ layout: key,
484
+ leaseHandle: fabricLeaseHandle
485
+ )]
486
+ }
487
+ // UIKit has no Fabric lease owner. A direct mounted view is still an
488
+ // exact immutable artifact for this semantic/physical key and remains
489
+ // valid after unmounted cache eviction.
490
+ guard let owner = directOwnersByLayout[key]?.first else { return nil }
491
+ return directMounted[owner]
492
+ }
493
+
494
+ /// Lookup after same-owner mounted and completed entries. Pending, mounted,
495
+ /// and direct owners all retain the same immutable artifact contract even
496
+ /// if the completed cache was evicted or cleared under memory pressure.
497
+ private func liveLayoutLocked(for key: ProseLayoutKey) -> PreparedProseLayout? {
498
+ if let leaseKey = pendingLeaseKeysByLayout[key]?.first, let layout = pendingLeases[leaseKey] {
499
+ return layout
500
+ }
501
+ if let leaseKey = mountedLeaseKeysByLayout[key]?.first, let layout = mountedLeases[leaseKey] {
502
+ return layout
503
+ }
504
+ if let owner = directOwnersByLayout[key]?.first {
505
+ return directMounted[owner]
506
+ }
507
+ return nil
508
+ }
509
+
510
+ private func mountKey(for layoutKey: ProseLayoutKey) -> ProseMountKey {
511
+ ProseMountKey(generationIdentity: layoutKey.generationIdentity, widthPixels: layoutKey.widthPixels, displayScaleBits: layoutKey.displayScaleBits)
512
+ }
513
+
514
+ private func removeCompletedLocked(_ key: ProseLayoutKey) {
515
+ guard let layout = completed.removeValue(forKey: key) else { return }
516
+ accessGenerations.removeValue(forKey: key)
517
+ let mountKey = mountKey(for: key)
518
+ if mountIndex[mountKey] == key { mountIndex.removeValue(forKey: mountKey) }
519
+ updateOwnershipLocked(layout, completedDelta: -1, publicationKey: key)
520
+ }
521
+
522
+ private func enforceBudgetLocked() {
523
+ // Completed entries are a disposable LRU. A pending Yoga-to-Fabric
524
+ // handoff is an exact active owner: evicting it would make Fabric's
525
+ // later mount miss without any completed-cache fallback. Its lifecycle
526
+ // release, activation replacement, or explicit memory warning owns
527
+ // retirement instead of ordinary byte pressure.
528
+ while budgetedRetainedBytes > byteBudget {
529
+ if let oldest = oldestCompletedKeyLocked() {
530
+ removeCompletedLocked(oldest)
531
+ continue
532
+ }
533
+ break
534
+ }
535
+ retireUnownedPublicationKeysLocked()
536
+ publishOwnerBytesLocked()
537
+ }
538
+
539
+ private func retireUnownedPublicationKeysLocked() {
540
+ #if DEBUG
541
+ for key in publicationRetirementCandidates where livePublicationCounts[key, default: 0] == 0 {
542
+ publishedKeys.remove(key)
543
+ }
544
+ publicationRetirementCandidates.removeAll()
545
+ #endif
546
+ }
547
+
548
+ private func publishOwnerBytesLocked() {
549
+ PreparedProseInstrumentation.cacheUpdated(
550
+ unmountedBytes: unmountedRetainedBytes,
551
+ unmountedResidentCount: completed.count
552
+ )
553
+ PreparedProseInstrumentation.retained(.unmountedLayout, scope: "cache", bytes: unmountedRetainedBytes)
554
+ PreparedProseInstrumentation.retained(.fabricLeaseHandoff, scope: "leases", bytes: fabricLeaseRetainedBytes)
555
+ PreparedProseInstrumentation.retained(.directMounted, scope: "views", bytes: directMountedRetainedBytes)
556
+ }
557
+
558
+ private func insertCompletedLocked(_ layout: PreparedProseLayout, for key: ProseLayoutKey) {
559
+ if completed[key] != nil {
560
+ removeCompletedLocked(key)
561
+ }
562
+ completed[key] = layout
563
+ mountIndex[mountKey(for: key)] = key
564
+ updateOwnershipLocked(layout, completedDelta: 1, publicationKey: key)
565
+ touch(key)
566
+ }
567
+
568
+ private func insertPendingLeaseLocked(_ layout: PreparedProseLayout, for key: FabricLeaseKey) {
569
+ removePendingLeaseLocked(key)
570
+ pendingLeases[key] = layout
571
+ insertLeaseKey(key, into: &pendingLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
572
+ insertLeaseKey(key, into: &pendingLeaseKeysBySurface, key: key.surface)
573
+ insertLeaseKey(key, into: &pendingLeaseKeysByLayout, key: key.layout)
574
+ if layout.retainedBytes > byteBudget { oversizedLeaseCount += 1 }
575
+ updateOwnershipLocked(layout, pendingDelta: 1, publicationKey: key.layout)
576
+ }
577
+
578
+ private func insertMountedLeaseLocked(_ layout: PreparedProseLayout, for key: FabricLeaseKey) {
579
+ removeMountedLeaseLocked(key)
580
+ mountedLeases[key] = layout
581
+ insertLeaseKey(key, into: &mountedLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
582
+ insertLeaseKey(key, into: &mountedLeaseKeysBySurface, key: key.surface)
583
+ insertLeaseKey(key, into: &mountedLeaseKeysByLayout, key: key.layout)
584
+ if layout.retainedBytes > byteBudget { oversizedLeaseCount += 1 }
585
+ updateOwnershipLocked(layout, mountedDelta: 1, publicationKey: key.layout)
586
+ }
587
+
588
+ private func removeMountedLeaseLocked(_ key: FabricLeaseKey) {
589
+ guard let layout = mountedLeases.removeValue(forKey: key) else { return }
590
+ removeLeaseKey(key, from: &mountedLeaseKeysByOwner, key: FabricLeaseOwner(surface: key.surface, leaseHandle: key.leaseHandle))
591
+ removeLeaseKey(key, from: &mountedLeaseKeysBySurface, key: key.surface)
592
+ removeLeaseKey(key, from: &mountedLeaseKeysByLayout, key: key.layout)
593
+ if layout.retainedBytes > byteBudget { oversizedLeaseCount -= 1 }
594
+ updateOwnershipLocked(layout, mountedDelta: -1, publicationKey: key.layout)
595
+ }
596
+
597
+ private func insertDirectMountLocked(_ layout: PreparedProseLayout, for owner: String) {
598
+ removeDirectMountLocked(owner)
599
+ directMounted[owner] = layout
600
+ directOwnersByLayout[layout.key, default: []].insert(owner)
601
+ updateOwnershipLocked(layout, directDelta: 1, publicationKey: layout.key)
602
+ }
603
+
604
+ private func removeDirectMountLocked(_ owner: String) {
605
+ guard let layout = directMounted.removeValue(forKey: owner) else { return }
606
+ directOwnersByLayout[layout.key]?.remove(owner)
607
+ if directOwnersByLayout[layout.key]?.isEmpty == true { directOwnersByLayout.removeValue(forKey: layout.key) }
608
+ updateOwnershipLocked(layout, directDelta: -1, publicationKey: layout.key)
609
+ }
610
+
611
+ private func insertLeaseKey<Index: Hashable>(
612
+ _ key: FabricLeaseKey,
613
+ into table: inout [Index: Set<FabricLeaseKey>],
614
+ key indexKey: Index
615
+ ) {
616
+ table[indexKey, default: []].insert(key)
617
+ }
618
+
619
+ private func removeLeaseKey<Index: Hashable>(
620
+ _ key: FabricLeaseKey,
621
+ from table: inout [Index: Set<FabricLeaseKey>],
622
+ key indexKey: Index
623
+ ) {
624
+ table[indexKey]?.remove(key)
625
+ if table[indexKey]?.isEmpty == true { table.removeValue(forKey: indexKey) }
626
+ }
627
+
628
+ private func matchingLeaseKeysLocked(
629
+ in keys: Set<FabricLeaseKey>,
630
+ generationIdentity: String?,
631
+ leaseHandle: UInt64?
632
+ ) -> [FabricLeaseKey] {
633
+ keys.filter {
634
+ (generationIdentity == nil || $0.layout.generationIdentity == generationIdentity) &&
635
+ (leaseHandle == nil || $0.leaseHandle == leaseHandle)
636
+ }
637
+ }
638
+
639
+ private func oldestCompletedKeyLocked() -> ProseLayoutKey? {
640
+ var skippedPinnedEntry = false
641
+ while accessOrderHead < accessOrder.count {
642
+ let token = accessOrder[accessOrderHead]
643
+ accessOrderHead += 1
644
+ guard accessGenerations[token.key] == token.generation,
645
+ let layout = completed[token.key],
646
+ let ownership = ownershipByIdentifier[ObjectIdentifier(layout)]
647
+ else { continue }
648
+
649
+ if ownership.pendingReferences == 0,
650
+ ownership.mountedReferences == 0,
651
+ ownership.directReferences == 0 {
652
+ // A pinned entry ahead of this candidate still needs its LRU
653
+ // token when it later returns to the budget. Rebuild only on
654
+ // this cold eviction path to preserve that ordering.
655
+ if skippedPinnedEntry {
656
+ rebuildAccessOrderLocked(excluding: token)
657
+ } else {
658
+ compactAccessOrderIfNeededLocked()
659
+ }
660
+ return token.key
661
+ }
662
+
663
+ skippedPinnedEntry = true
664
+ }
665
+ // Every remaining completed entry is pinned. Keep their current LRU
666
+ // tokens so a later release can make one evictable without a new hit.
667
+ rebuildAccessOrderLocked()
668
+ return nil
669
+ }
670
+
671
+ private func rebuildAccessOrderLocked(
672
+ excluding excluded: (key: ProseLayoutKey, generation: UInt64)? = nil
673
+ ) {
674
+ accessOrder = accessOrder.filter { token in
675
+ guard accessGenerations[token.key] == token.generation, completed[token.key] != nil else {
676
+ return false
677
+ }
678
+ return excluded.map { $0.key != token.key || $0.generation != token.generation } ?? true
679
+ }
680
+ accessOrderHead = 0
681
+ }
682
+
683
+ private func compactAccessOrderIfNeededLocked() {
684
+ let liveTokenCount = accessOrder.count - accessOrderHead
685
+ let compactionThreshold = max(64, completed.count * 3)
686
+ guard liveTokenCount > compactionThreshold || accessOrderHead > compactionThreshold else { return }
687
+ accessOrder = accessOrder[accessOrderHead...].filter { token in
688
+ accessGenerations[token.key] == token.generation && completed[token.key] != nil
689
+ }
690
+ accessOrderHead = 0
691
+ }
692
+
693
+ private func updateOwnershipLocked(
694
+ _ layout: PreparedProseLayout,
695
+ completedDelta: Int = 0,
696
+ pendingDelta: Int = 0,
697
+ mountedDelta: Int = 0,
698
+ directDelta: Int = 0,
699
+ publicationKey: ProseLayoutKey
700
+ ) {
701
+ let identifier = ObjectIdentifier(layout)
702
+ var ownership = ownershipByIdentifier[identifier] ?? LayoutOwnership(layout: layout)
703
+ let previous = ownership
704
+ ownership.completedReferences += completedDelta
705
+ ownership.pendingReferences += pendingDelta
706
+ ownership.mountedReferences += mountedDelta
707
+ ownership.directReferences += directDelta
708
+ precondition(
709
+ ownership.completedReferences >= 0 && ownership.pendingReferences >= 0 &&
710
+ ownership.mountedReferences >= 0 && ownership.directReferences >= 0,
711
+ "Prepared prose ownership references must not underflow."
712
+ )
713
+ applyOwnershipContributionDelta(from: previous, to: ownership)
714
+ if ownership.completedReferences + ownership.pendingReferences + ownership.mountedReferences + ownership.directReferences == 0 {
715
+ ownershipByIdentifier.removeValue(forKey: identifier)
716
+ } else {
717
+ ownershipByIdentifier[identifier] = ownership
718
+ }
719
+ #if DEBUG
720
+ let previousLiveCount = livePublicationCounts[publicationKey, default: 0]
721
+ let nextLiveCount = previousLiveCount + completedDelta + pendingDelta + mountedDelta + directDelta
722
+ precondition(nextLiveCount >= 0, "Prepared prose publication references must not underflow.")
723
+ if nextLiveCount == 0 {
724
+ livePublicationCounts.removeValue(forKey: publicationKey)
725
+ publicationRetirementCandidates.insert(publicationKey)
726
+ } else {
727
+ livePublicationCounts[publicationKey] = nextLiveCount
728
+ publicationRetirementCandidates.remove(publicationKey)
729
+ }
730
+ #endif
731
+ }
732
+
733
+ private func applyOwnershipContributionDelta(from previous: LayoutOwnership, to next: LayoutOwnership) {
734
+ let bytes = next.layout.retainedBytes
735
+ retainedBytes += contribution(next, bytes: bytes, kind: .retained) - contribution(previous, bytes: bytes, kind: .retained)
736
+ budgetedRetainedBytes += contribution(next, bytes: bytes, kind: .budgeted) - contribution(previous, bytes: bytes, kind: .budgeted)
737
+ unmountedRetainedBytes += contribution(next, bytes: bytes, kind: .unmounted) - contribution(previous, bytes: bytes, kind: .unmounted)
738
+ fabricLeaseRetainedBytes += contribution(next, bytes: bytes, kind: .fabricLease) - contribution(previous, bytes: bytes, kind: .fabricLease)
739
+ directMountedRetainedBytes += contribution(next, bytes: bytes, kind: .directMounted) - contribution(previous, bytes: bytes, kind: .directMounted)
740
+ }
741
+
742
+ private enum OwnershipContribution {
743
+ case retained
744
+ case budgeted
745
+ case unmounted
746
+ case fabricLease
747
+ case directMounted
748
+ }
749
+
750
+ private func contribution(_ ownership: LayoutOwnership, bytes: Int, kind: OwnershipContribution) -> Int {
751
+ switch kind {
752
+ case .retained:
753
+ return ownership.completedReferences + ownership.pendingReferences + ownership.mountedReferences + ownership.directReferences > 0 ? bytes : 0
754
+ case .budgeted:
755
+ return ownership.completedReferences + ownership.pendingReferences > 0 &&
756
+ ownership.mountedReferences + ownership.directReferences == 0 ? bytes : 0
757
+ case .unmounted:
758
+ return ownership.completedReferences > 0 && ownership.pendingReferences == 0 &&
759
+ ownership.mountedReferences == 0 && ownership.directReferences == 0 ? bytes : 0
760
+ case .fabricLease:
761
+ return ownership.pendingReferences + ownership.mountedReferences > 0 && ownership.directReferences == 0 ? bytes : 0
762
+ case .directMounted:
763
+ return ownership.directReferences > 0 ? bytes : 0
764
+ }
765
+ }
766
+ }