@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,851 @@
1
+ package com.apollohg.editor
2
+
3
+ import android.content.Context
4
+ import android.content.res.ColorStateList
5
+ import android.graphics.Color
6
+ import android.graphics.Typeface
7
+ import android.graphics.drawable.GradientDrawable
8
+ import android.graphics.drawable.RippleDrawable
9
+ import android.util.TypedValue
10
+ import android.view.Gravity
11
+ import android.view.View
12
+ import android.view.ViewOutlineProvider
13
+ import android.widget.FrameLayout
14
+ import android.widget.HorizontalScrollView
15
+ import android.widget.LinearLayout
16
+ import androidx.appcompat.R as AppCompatR
17
+ import androidx.appcompat.content.res.AppCompatResources
18
+ import androidx.appcompat.view.ContextThemeWrapper
19
+ import androidx.appcompat.widget.AppCompatButton
20
+ import androidx.appcompat.widget.PopupMenu
21
+ import androidx.core.view.setPadding
22
+ import com.google.android.material.R as MaterialR
23
+ import com.google.android.material.color.DynamicColors
24
+ import kotlin.math.roundToInt
25
+
26
+ internal class EditorKeyboardToolbarView(context: Context) : FrameLayout(context) {
27
+ private companion object {
28
+ private const val NATIVE_CONTAINER_HEIGHT_DP = 68
29
+ private const val NATIVE_CONTAINER_HORIZONTAL_PADDING_DP = 16
30
+ private const val NATIVE_CONTAINER_VERTICAL_PADDING_DP = 14
31
+ private const val NATIVE_BUTTON_SIZE_DP = 40
32
+ private const val NATIVE_BUTTON_ICON_SIZE_SP = 24f
33
+ private const val NATIVE_ITEM_SPACING_DP = 8
34
+ private const val NATIVE_GROUP_SPACING_DP = 12
35
+ }
36
+
37
+ private data class ButtonBinding(val item: NativeToolbarItem, val button: AppCompatButton)
38
+
39
+ var onPressItem: ((NativeToolbarItem) -> Unit)? = null
40
+ var onSelectMentionSuggestion: ((NativeMentionSuggestion) -> Unit)? = null
41
+
42
+ private val customThemedContext: Context = DynamicColors.wrapContextIfAvailable(context)
43
+ private val nativeThemedContext: Context = DynamicColors.wrapContextIfAvailable(
44
+ ContextThemeWrapper(context, MaterialR.style.Theme_Material3_DayNight)
45
+ )
46
+ private val rootRow = LinearLayout(context)
47
+ private val startRow = LinearLayout(context)
48
+ private val centerScrollView = HorizontalScrollView(context)
49
+ private val contentRow = LinearLayout(context)
50
+ private val endRow = LinearLayout(context)
51
+ private var theme: EditorToolbarTheme? = null
52
+ private var mentionTheme: EditorMentionTheme? = null
53
+ private var state: NativeToolbarState = NativeToolbarState.empty
54
+ private var items: List<NativeToolbarItem> = NativeToolbarItem.defaults
55
+ private var mentionTrigger: String = "@"
56
+ private var mentionSuggestions: List<NativeMentionSuggestion> = emptyList()
57
+ private var expandedGroupKey: String? = null
58
+ private var rebuildGeneration: Int = 0
59
+ private val bindings = mutableListOf<ButtonBinding>()
60
+ private val separators = mutableListOf<View>()
61
+ private val mentionChips = mutableListOf<MentionSuggestionChipView>()
62
+ private val buttonBackgroundColors = mutableMapOf<AppCompatButton, Int>()
63
+ private val buttonCornerRadii = mutableMapOf<AppCompatButton, Float>()
64
+ private val density = resources.displayMetrics.density
65
+ internal var appliedAppearance: EditorToolbarAppearance = EditorToolbarAppearance.CUSTOM
66
+ private set
67
+ internal var appliedChromeCornerRadiusPx: Float = 0f
68
+ private set
69
+ internal var appliedChromeStrokeWidthPx: Int = 0
70
+ private set
71
+ internal var appliedChromeElevationPx: Float = 0f
72
+ private set
73
+ internal var appliedChromeColor: Int = Color.TRANSPARENT
74
+ private set
75
+ internal var appliedButtonCornerRadiusPx: Float = 0f
76
+ private set
77
+ val isShowingMentionSuggestions: Boolean
78
+ get() = mentionSuggestions.isNotEmpty()
79
+
80
+ init {
81
+ setBackgroundColor(Color.TRANSPARENT)
82
+ clipToPadding = false
83
+ clipChildren = false
84
+
85
+ rootRow.orientation = LinearLayout.HORIZONTAL
86
+ rootRow.gravity = Gravity.CENTER_VERTICAL
87
+ rootRow.clipToPadding = false
88
+ rootRow.clipChildren = false
89
+ startRow.orientation = LinearLayout.HORIZONTAL
90
+ startRow.gravity = Gravity.START or Gravity.CENTER_VERTICAL
91
+ endRow.orientation = LinearLayout.HORIZONTAL
92
+ endRow.gravity = Gravity.END or Gravity.CENTER_VERTICAL
93
+ centerScrollView.isHorizontalScrollBarEnabled = false
94
+ centerScrollView.overScrollMode = OVER_SCROLL_NEVER
95
+ centerScrollView.clipToPadding = false
96
+ centerScrollView.clipChildren = true
97
+ contentRow.orientation = LinearLayout.HORIZONTAL
98
+ contentRow.gravity = Gravity.START or Gravity.CENTER_VERTICAL
99
+ contentRow.clipToPadding = false
100
+ contentRow.clipChildren = false
101
+ centerScrollView.addView(
102
+ contentRow,
103
+ LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
104
+ )
105
+ rootRow.addView(
106
+ startRow,
107
+ LinearLayout.LayoutParams(
108
+ LinearLayout.LayoutParams.WRAP_CONTENT,
109
+ LinearLayout.LayoutParams.WRAP_CONTENT
110
+ )
111
+ )
112
+ rootRow.addView(
113
+ centerScrollView,
114
+ LinearLayout.LayoutParams(
115
+ 0,
116
+ LinearLayout.LayoutParams.WRAP_CONTENT,
117
+ 1f
118
+ )
119
+ )
120
+ rootRow.addView(
121
+ endRow,
122
+ LinearLayout.LayoutParams(
123
+ LinearLayout.LayoutParams.WRAP_CONTENT,
124
+ LinearLayout.LayoutParams.WRAP_CONTENT
125
+ )
126
+ )
127
+ addView(
128
+ rootRow,
129
+ LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
130
+ )
131
+ rebuildContent(preserveScrollPosition = false)
132
+ }
133
+
134
+ fun setItems(items: List<NativeToolbarItem>) {
135
+ this.items = compactItems(items)
136
+ if (expandedGroupKey != null && !containsExpandableGroup(this.items, expandedGroupKey)) {
137
+ expandedGroupKey = null
138
+ }
139
+ if (!isShowingMentionSuggestions) {
140
+ rebuildContent()
141
+ }
142
+ }
143
+
144
+ fun applyTheme(theme: EditorToolbarTheme?) {
145
+ val previousAppearance = this.theme?.appearance ?: EditorToolbarAppearance.CUSTOM
146
+ val nextAppearance = theme?.appearance ?: EditorToolbarAppearance.CUSTOM
147
+ this.theme = theme
148
+ if (previousAppearance != nextAppearance) {
149
+ rebuildContent()
150
+ return
151
+ }
152
+ updateChrome()
153
+ separators.forEach { separator ->
154
+ separator.setBackgroundColor(resolveSeparatorColor())
155
+ }
156
+ bindings.forEach { binding ->
157
+ updateButtonAppearance(
158
+ binding,
159
+ enabled = buttonState(binding.item, state).first,
160
+ active = buttonState(binding.item, state).second
161
+ )
162
+ }
163
+ mentionChips.forEach { chip ->
164
+ chip.applyTheme(mentionTheme, theme?.appearance ?: EditorToolbarAppearance.CUSTOM)
165
+ }
166
+ }
167
+
168
+ fun applyMentionTheme(theme: EditorMentionTheme?) {
169
+ mentionTheme = theme
170
+ mentionChips.forEach { chip ->
171
+ chip.applyTheme(theme, this.theme?.appearance ?: EditorToolbarAppearance.CUSTOM)
172
+ }
173
+ }
174
+
175
+ fun applyState(state: NativeToolbarState) {
176
+ this.state = state
177
+ bindings.forEach { binding ->
178
+ val (enabled, active) = buttonState(binding.item, state)
179
+ binding.button.isEnabled = enabled
180
+ binding.button.isSelected = active
181
+ updateButtonAppearance(binding, enabled, active)
182
+ }
183
+ }
184
+
185
+ fun setMentionSuggestions(
186
+ suggestions: List<NativeMentionSuggestion>,
187
+ trigger: String = "@"
188
+ ): Boolean {
189
+ val hadSuggestions = isShowingMentionSuggestions
190
+ mentionTrigger = trigger
191
+ mentionSuggestions = suggestions.take(8)
192
+ if (hadSuggestions && isShowingMentionSuggestions) {
193
+ updateMentionSuggestionsInPlace()
194
+ updateChrome()
195
+ applyState(state)
196
+ } else {
197
+ rebuildContent(preserveScrollPosition = hadSuggestions == isShowingMentionSuggestions)
198
+ }
199
+ return hadSuggestions != isShowingMentionSuggestions
200
+ }
201
+
202
+ fun triggerMentionSuggestionTapForTesting(index: Int) {
203
+ mentionChips.getOrNull(index)?.performClick()
204
+ }
205
+
206
+ internal fun buttonAtForTesting(index: Int): AppCompatButton? =
207
+ bindings.getOrNull(index)?.button
208
+
209
+ internal fun buttonCountForTesting(): Int = bindings.size
210
+
211
+ internal fun buttonLabelsForPlacementForTesting(placement: ToolbarItemPlacement): List<String> {
212
+ val row = when (placement) {
213
+ ToolbarItemPlacement.START -> startRow
214
+ ToolbarItemPlacement.SCROLL -> contentRow
215
+ ToolbarItemPlacement.END -> endRow
216
+ }
217
+ return (0 until row.childCount).mapNotNull { index ->
218
+ (row.getChildAt(index) as? AppCompatButton)?.contentDescription?.toString()
219
+ }
220
+ }
221
+
222
+ internal fun buttonBackgroundColorAtForTesting(index: Int): Int? =
223
+ bindings.getOrNull(index)?.button?.let { buttonBackgroundColors[it] }
224
+
225
+ internal fun buttonCornerRadiusAtForTesting(index: Int): Float? =
226
+ bindings.getOrNull(index)?.button?.let { buttonCornerRadii[it] }
227
+
228
+ internal fun mentionChipAtForTesting(index: Int): MentionSuggestionChipView? =
229
+ mentionChips.getOrNull(index)
230
+
231
+ internal fun separatorAtForTesting(index: Int): View? = separators.getOrNull(index)
232
+
233
+ private fun rebuildContent(preserveScrollPosition: Boolean = true) {
234
+ val targetScrollX = if (preserveScrollPosition) centerScrollView.scrollX else 0
235
+ val generation = ++rebuildGeneration
236
+ bindings.clear()
237
+ buttonBackgroundColors.clear()
238
+ buttonCornerRadii.clear()
239
+ separators.clear()
240
+ mentionChips.clear()
241
+ contentRow.removeAllViews()
242
+ startRow.removeAllViews()
243
+ endRow.removeAllViews()
244
+
245
+ if (isShowingMentionSuggestions) {
246
+ val visibleItems = visibleItemsByPlacement()
247
+ rebuildButtonPlacement(visibleItems.start, startRow)
248
+ rebuildMentionSuggestions()
249
+ rebuildButtonPlacement(visibleItems.end, endRow)
250
+ } else {
251
+ rebuildButtons()
252
+ }
253
+
254
+ updateChrome()
255
+ applyState(state)
256
+ post {
257
+ if (generation != rebuildGeneration) return@post
258
+ val contentWidth = contentRow.width
259
+ val viewportWidth = (
260
+ centerScrollView.width - centerScrollView.paddingLeft -
261
+ centerScrollView.paddingRight
262
+ ).coerceAtLeast(0)
263
+ val maxScrollX = (contentWidth - viewportWidth).coerceAtLeast(0)
264
+ centerScrollView.scrollTo(targetScrollX.coerceIn(0, maxScrollX), 0)
265
+ }
266
+ }
267
+
268
+ private fun rebuildButtons() {
269
+ val visibleItems = visibleItemsByPlacement()
270
+ rebuildButtonPlacement(visibleItems.start, startRow)
271
+ rebuildButtonPlacement(visibleItems.scroll, contentRow)
272
+ rebuildButtonPlacement(visibleItems.end, endRow)
273
+ }
274
+
275
+ private fun rebuildButtonPlacement(items: List<NativeToolbarItem>, container: LinearLayout) {
276
+ val themedContext = currentThemedContext()
277
+ for (item in items) {
278
+ if (item.type == ToolbarItemKind.SEPARATOR) {
279
+ val separator = View(context)
280
+ configureSeparator(separator)
281
+ separators.add(separator)
282
+ container.addView(separator)
283
+ continue
284
+ }
285
+
286
+ val button = AppCompatButton(themedContext).apply {
287
+ val resolvedIcon = item.icon?.resolveForAndroid(themedContext)
288
+ ?: NativeToolbarResolvedIcon("?")
289
+ text = resolvedIcon.text
290
+ typeface = resolvedIcon.typeface ?: Typeface.DEFAULT
291
+ gravity = Gravity.CENTER
292
+ background = GradientDrawable()
293
+ isAllCaps = false
294
+ includeFontPadding = false
295
+ contentDescription = item.label
296
+ setOnClickListener {
297
+ when (item.type) {
298
+ ToolbarItemKind.GROUP -> handleGroupButtonPress(this, item)
299
+
300
+ else -> {
301
+ onPressItem?.invoke(item.copy(parentGroupKey = null))
302
+ if (item.parentGroupKey != null &&
303
+ expandedGroupKey == item.parentGroupKey
304
+ ) {
305
+ expandedGroupKey = null
306
+ rebuildContent()
307
+ }
308
+ }
309
+ }
310
+ }
311
+ elevation = 0f
312
+ translationZ = 0f
313
+ stateListAnimator = null
314
+ }
315
+ val params = LinearLayout.LayoutParams(
316
+ LinearLayout.LayoutParams.WRAP_CONTENT,
317
+ LinearLayout.LayoutParams.WRAP_CONTENT
318
+ )
319
+ button.layoutParams = params
320
+ val binding = ButtonBinding(item, button)
321
+ applyButtonLayout(
322
+ binding,
323
+ appearance =
324
+ theme?.appearance ?: EditorToolbarAppearance.CUSTOM
325
+ )
326
+ bindings.add(binding)
327
+ container.addView(button)
328
+ }
329
+ }
330
+
331
+ private fun rebuildMentionSuggestions() {
332
+ val themedContext = currentThemedContext()
333
+ for (suggestion in mentionSuggestions) {
334
+ val chip = MentionSuggestionChipView(themedContext, suggestion, mentionTrigger).apply {
335
+ applyTheme(mentionTheme, theme?.appearance ?: EditorToolbarAppearance.CUSTOM)
336
+ setOnClickListener { onSelectMentionSuggestion?.invoke(suggestion) }
337
+ }
338
+ val params = LinearLayout.LayoutParams(
339
+ LinearLayout.LayoutParams.WRAP_CONTENT,
340
+ LinearLayout.LayoutParams.WRAP_CONTENT
341
+ )
342
+ params.marginEnd = dp(8)
343
+ chip.layoutParams = params
344
+ mentionChips.add(chip)
345
+ contentRow.addView(chip)
346
+ }
347
+ }
348
+
349
+ private fun updateMentionSuggestionsInPlace() {
350
+ val themedContext = currentThemedContext()
351
+ val existingByKey = mentionChips.associateBy { it.suggestion.key }.toMutableMap()
352
+ val nextChips = mentionSuggestions.map { suggestion ->
353
+ existingByKey.remove(suggestion.key)?.also {
354
+ it.updateSuggestion(suggestion, mentionTrigger)
355
+ } ?: MentionSuggestionChipView(themedContext, suggestion, mentionTrigger)
356
+ }
357
+
358
+ mentionChips.filterNot { existing -> nextChips.any { it === existing } }.forEach {
359
+ contentRow.removeView(it)
360
+ }
361
+ nextChips.forEachIndexed { index, chip ->
362
+ if (chip.layoutParams !is LinearLayout.LayoutParams) {
363
+ chip.layoutParams = LinearLayout.LayoutParams(
364
+ LinearLayout.LayoutParams.WRAP_CONTENT,
365
+ LinearLayout.LayoutParams.WRAP_CONTENT
366
+ ).apply {
367
+ marginEnd = dp(8)
368
+ }
369
+ }
370
+ chip.applyTheme(mentionTheme, theme?.appearance ?: EditorToolbarAppearance.CUSTOM)
371
+ chip.setOnClickListener { onSelectMentionSuggestion?.invoke(chip.suggestion) }
372
+ if (chip.parent == null) {
373
+ contentRow.addView(chip, index)
374
+ } else if (contentRow.indexOfChild(chip) != index) {
375
+ contentRow.removeView(chip)
376
+ contentRow.addView(chip, index)
377
+ }
378
+ }
379
+ mentionChips.clear()
380
+ mentionChips.addAll(nextChips)
381
+ }
382
+
383
+ private fun compactItems(items: List<NativeToolbarItem>): List<NativeToolbarItem> {
384
+ return items.filterIndexed { index, item ->
385
+ if (item.type != ToolbarItemKind.SEPARATOR) return@filterIndexed true
386
+ index > 0 &&
387
+ index < items.lastIndex &&
388
+ items[index - 1].type != ToolbarItemKind.SEPARATOR &&
389
+ items[index + 1].type != ToolbarItemKind.SEPARATOR
390
+ }
391
+ }
392
+
393
+ private fun visibleItems(): List<NativeToolbarItem> {
394
+ val visible = mutableListOf<NativeToolbarItem>()
395
+ for (item in compactItems(items)) {
396
+ visible += item
397
+ if (
398
+ item.type == ToolbarItemKind.GROUP &&
399
+ (item.presentation ?: ToolbarGroupPresentation.EXPAND) ==
400
+ ToolbarGroupPresentation.EXPAND &&
401
+ expandedGroupKey == item.key
402
+ ) {
403
+ visible += item.items.map { child ->
404
+ child.copy(
405
+ parentGroupKey = item.key,
406
+ placement =
407
+ child.placement ?: item.placement
408
+ )
409
+ }
410
+ }
411
+ }
412
+ return compactItems(visible)
413
+ }
414
+
415
+ private data class VisibleToolbarItemsByPlacement(
416
+ val start: List<NativeToolbarItem>,
417
+ val scroll: List<NativeToolbarItem>,
418
+ val end: List<NativeToolbarItem>
419
+ )
420
+
421
+ private fun visibleItemsByPlacement(): VisibleToolbarItemsByPlacement {
422
+ val start = mutableListOf<NativeToolbarItem>()
423
+ val scroll = mutableListOf<NativeToolbarItem>()
424
+ val end = mutableListOf<NativeToolbarItem>()
425
+ for (item in visibleItems()) {
426
+ when (item.placement ?: ToolbarItemPlacement.SCROLL) {
427
+ ToolbarItemPlacement.START -> start += item
428
+ ToolbarItemPlacement.END -> end += item
429
+ ToolbarItemPlacement.SCROLL -> scroll += item
430
+ }
431
+ }
432
+ return VisibleToolbarItemsByPlacement(
433
+ start = compactItems(start),
434
+ scroll = compactItems(scroll),
435
+ end = compactItems(end)
436
+ )
437
+ }
438
+
439
+ private fun containsExpandableGroup(items: List<NativeToolbarItem>, key: String?): Boolean {
440
+ key ?: return false
441
+ return items.any {
442
+ it.type == ToolbarItemKind.GROUP &&
443
+ it.key == key &&
444
+ (it.presentation ?: ToolbarGroupPresentation.EXPAND) ==
445
+ ToolbarGroupPresentation.EXPAND
446
+ }
447
+ }
448
+
449
+ private fun handleGroupButtonPress(anchor: View, item: NativeToolbarItem) {
450
+ if (item.items.isEmpty()) return
451
+ when (item.presentation ?: ToolbarGroupPresentation.EXPAND) {
452
+ ToolbarGroupPresentation.EXPAND -> {
453
+ val key = item.key ?: return
454
+ expandedGroupKey = if (expandedGroupKey == key) null else key
455
+ rebuildContent()
456
+ }
457
+
458
+ ToolbarGroupPresentation.MENU -> showGroupMenu(anchor, item)
459
+ }
460
+ }
461
+
462
+ private fun showGroupMenu(anchor: View, item: NativeToolbarItem) {
463
+ val popupMenu = PopupMenu(currentThemedContext(), anchor)
464
+ item.items.forEachIndexed { index, child ->
465
+ val (enabled, active) = buttonState(child, state)
466
+ val menuItem = popupMenu.menu.add(0, index, index, child.label ?: child.key ?: "Item")
467
+ menuItem.isEnabled = enabled
468
+ menuItem.isCheckable = true
469
+ menuItem.isChecked = active
470
+ }
471
+ popupMenu.setOnMenuItemClickListener { menuItem ->
472
+ val child =
473
+ item.items.getOrNull(menuItem.itemId) ?: return@setOnMenuItemClickListener false
474
+ onPressItem?.invoke(child)
475
+ true
476
+ }
477
+ popupMenu.show()
478
+ }
479
+
480
+ private fun updateChrome() {
481
+ val appearance = theme?.appearance ?: EditorToolbarAppearance.CUSTOM
482
+ val cornerRadiusPx = (theme?.resolvedBorderRadius() ?: 0f) * density
483
+ val strokeWidthPx = if (appearance == EditorToolbarAppearance.NATIVE) {
484
+ 0
485
+ } else {
486
+ physicalToolbarBorderWidth(theme?.resolvedBorderWidth() ?: 1f, density)
487
+ }
488
+ val drawable = GradientDrawable().apply {
489
+ shape = GradientDrawable.RECTANGLE
490
+ cornerRadius = cornerRadiusPx
491
+ setColor(
492
+ if (appearance == EditorToolbarAppearance.NATIVE) {
493
+ resolveColorAttr(
494
+ MaterialR.attr.colorSurfaceContainer,
495
+ MaterialR.attr.colorSurfaceContainerLow,
496
+ MaterialR.attr.colorSurface,
497
+ android.R.attr.colorBackground
498
+ )
499
+ } else {
500
+ theme?.backgroundColor ?: resolveColorAttr(
501
+ MaterialR.attr.colorSurface,
502
+ android.R.attr.colorBackground
503
+ )
504
+ }
505
+ )
506
+ if (strokeWidthPx > 0) {
507
+ setStroke(strokeWidthPx, theme?.borderColor ?: resolveSeparatorColor())
508
+ }
509
+ }
510
+ appliedAppearance = appearance
511
+ appliedChromeCornerRadiusPx = cornerRadiusPx
512
+ appliedChromeStrokeWidthPx = strokeWidthPx
513
+ appliedChromeElevationPx = 0f
514
+ appliedChromeColor = if (appearance == EditorToolbarAppearance.NATIVE) {
515
+ resolveColorAttr(
516
+ MaterialR.attr.colorSurfaceContainer,
517
+ MaterialR.attr.colorSurfaceContainerLow,
518
+ MaterialR.attr.colorSurface,
519
+ android.R.attr.colorBackground
520
+ )
521
+ } else {
522
+ theme?.backgroundColor ?: resolveColorAttr(
523
+ MaterialR.attr.colorSurface,
524
+ android.R.attr.colorBackground
525
+ )
526
+ }
527
+ background = drawable
528
+ outlineProvider = ViewOutlineProvider.BACKGROUND
529
+ clipToOutline = cornerRadiusPx > 0f
530
+ elevation = appliedChromeElevationPx
531
+ updateContainerLayout(appearance)
532
+ separators.forEach(::configureSeparator)
533
+ }
534
+
535
+ private fun updateButtonAppearance(binding: ButtonBinding, enabled: Boolean, active: Boolean) {
536
+ val button = binding.button
537
+ val buttonStyle = binding.item.buttonStyle
538
+ val appearance = theme?.appearance ?: EditorToolbarAppearance.CUSTOM
539
+ applyButtonLayout(binding, appearance)
540
+ val textColor = when {
541
+ !enabled ->
542
+ buttonStyle?.disabledColor
543
+ ?: theme?.buttonDisabledColor
544
+ ?: withAlpha(
545
+ resolveColorAttr(
546
+ MaterialR.attr.colorOnSurface,
547
+ android.R.attr.textColorPrimary
548
+ ),
549
+ 0.38f
550
+ )
551
+
552
+ active ->
553
+ buttonStyle?.activeColor
554
+ ?: theme?.buttonActiveColor
555
+ ?: if (appearance == EditorToolbarAppearance.NATIVE) {
556
+ resolveColorAttr(
557
+ MaterialR.attr.colorOnSecondaryContainer,
558
+ MaterialR.attr.colorOnPrimaryContainer,
559
+ MaterialR.attr.colorOnSurface,
560
+ android.R.attr.textColorPrimary
561
+ )
562
+ } else {
563
+ resolveColorAttr(
564
+ AppCompatR.attr.colorPrimary,
565
+ android.R.attr.textColorPrimary
566
+ )
567
+ }
568
+
569
+ else ->
570
+ buttonStyle?.color
571
+ ?: theme?.buttonColor
572
+ ?: resolveColorAttr(
573
+ MaterialR.attr.colorOnSurfaceVariant,
574
+ MaterialR.attr.colorOnSurface,
575
+ android.R.attr.textColorSecondary
576
+ )
577
+ }
578
+ val inactiveBackgroundColor = buttonStyle?.backgroundColor
579
+ ?: theme?.buttonBackgroundColor
580
+ ?: Color.TRANSPARENT
581
+ val activeBackgroundColor = buttonStyle?.activeBackgroundColor
582
+ ?: theme?.buttonActiveBackgroundColor
583
+ ?: if (appearance == EditorToolbarAppearance.NATIVE) {
584
+ resolveColorAttr(
585
+ MaterialR.attr.colorSecondaryContainer,
586
+ MaterialR.attr.colorPrimaryContainer,
587
+ MaterialR.attr.colorSurfaceVariant,
588
+ android.R.attr.colorAccent
589
+ )
590
+ } else {
591
+ resolveColorAttr(
592
+ MaterialR.attr.colorPrimaryContainer,
593
+ MaterialR.attr.colorSecondaryContainer,
594
+ MaterialR.attr.colorSurfaceVariant,
595
+ android.R.attr.colorAccent
596
+ )
597
+ }
598
+ val disabledBackgroundColor = buttonStyle?.disabledBackgroundColor
599
+ ?: theme?.buttonDisabledBackgroundColor
600
+ ?: if (active) activeBackgroundColor else inactiveBackgroundColor
601
+ val backgroundColor = when {
602
+ !enabled -> disabledBackgroundColor
603
+ active -> activeBackgroundColor
604
+ else -> inactiveBackgroundColor
605
+ }
606
+ val defaultCornerRadius = theme?.resolvedButtonBorderRadius() ?: 6f
607
+ val buttonCornerRadiusDp = (buttonStyle?.borderRadius ?: defaultCornerRadius)
608
+ .takeIf { it.isFinite() }
609
+ ?.coerceAtLeast(0f)
610
+ ?: defaultCornerRadius
611
+ val buttonCornerRadiusPx = buttonCornerRadiusDp * density
612
+ val drawable = GradientDrawable().apply {
613
+ shape = GradientDrawable.RECTANGLE
614
+ cornerRadius = buttonCornerRadiusPx
615
+ setColor(backgroundColor)
616
+ }
617
+ appliedButtonCornerRadiusPx = buttonCornerRadiusPx
618
+ buttonBackgroundColors[button] = backgroundColor
619
+ buttonCornerRadii[button] = buttonCornerRadiusPx
620
+ button.background = drawable
621
+ ensureButtonRipple(button, buttonCornerRadiusPx)
622
+ button.setTextColor(textColor)
623
+ button.alpha = if (enabled || appearance == EditorToolbarAppearance.NATIVE) 1f else 0.7f
624
+ button.refreshDrawableState()
625
+ button.invalidate()
626
+ }
627
+
628
+ private fun buttonState(
629
+ item: NativeToolbarItem,
630
+ state: NativeToolbarState
631
+ ): Pair<Boolean, Boolean> {
632
+ return when (item.type) {
633
+ ToolbarItemKind.MARK -> {
634
+ val mark = item.mark.orEmpty()
635
+ Pair(state.allowedMarks.contains(mark), state.marks[mark] == true)
636
+ }
637
+
638
+ ToolbarItemKind.HEADING -> {
639
+ val level = item.headingLevel ?: return Pair(false, false)
640
+ Pair(
641
+ state.commands["toggleHeading$level"] == true,
642
+ state.nodes["h$level"] == true
643
+ )
644
+ }
645
+
646
+ ToolbarItemKind.BLOCKQUOTE -> Pair(
647
+ state.commands["toggleBlockquote"] == true,
648
+ state.nodes["blockquote"] == true
649
+ )
650
+
651
+ ToolbarItemKind.LIST -> when (item.listType) {
652
+ ToolbarListType.CAMEL_CASE_BULLET_LIST,
653
+ ToolbarListType.BULLET_LIST -> Pair(
654
+ state.commands["wrapBulletList"] == true,
655
+ state.nodes[item.listType.wireValue] == true
656
+ )
657
+
658
+ ToolbarListType.CAMEL_CASE_ORDERED_LIST,
659
+ ToolbarListType.ORDERED_LIST -> Pair(
660
+ state.commands["wrapOrderedList"] == true,
661
+ state.nodes[item.listType.wireValue] == true
662
+ )
663
+
664
+ null -> Pair(false, false)
665
+ }
666
+
667
+ ToolbarItemKind.COMMAND -> when (item.command) {
668
+ ToolbarCommand.INDENT_LIST -> Pair(state.commands["indentList"] == true, false)
669
+ ToolbarCommand.OUTDENT_LIST -> Pair(state.commands["outdentList"] == true, false)
670
+ ToolbarCommand.UNDO -> Pair(state.canUndo, false)
671
+ ToolbarCommand.REDO -> Pair(state.canRedo, false)
672
+ null -> Pair(false, false)
673
+ }
674
+
675
+ ToolbarItemKind.NODE -> {
676
+ val nodeType = item.nodeType.orEmpty()
677
+ Pair(state.insertableNodes.contains(nodeType), state.nodes[nodeType] == true)
678
+ }
679
+
680
+ ToolbarItemKind.ACTION -> Pair(!item.isDisabled, item.isActive)
681
+
682
+ ToolbarItemKind.GROUP -> Pair(
683
+ item.items.any { child -> buttonState(child, state).first },
684
+ item.items.any { child -> buttonState(child, state).second } ||
685
+ (
686
+ (item.presentation ?: ToolbarGroupPresentation.EXPAND) ==
687
+ ToolbarGroupPresentation.EXPAND &&
688
+ expandedGroupKey == item.key
689
+ )
690
+ )
691
+
692
+ ToolbarItemKind.SEPARATOR -> Pair(false, false)
693
+ }
694
+ }
695
+
696
+ private fun dp(value: Int): Int = (value * density).toInt()
697
+
698
+ private fun currentThemedContext(): Context =
699
+ if (theme?.appearance == EditorToolbarAppearance.NATIVE) {
700
+ nativeThemedContext
701
+ } else {
702
+ customThemedContext
703
+ }
704
+
705
+ private fun resolveColorAttr(vararg attrs: Int): Int =
706
+ resolveColorAttrOrNull(*attrs) ?: Color.TRANSPARENT
707
+
708
+ private fun resolveColorAttrOrNull(vararg attrs: Int): Int? {
709
+ val themedContext = currentThemedContext()
710
+ val typedValue = TypedValue()
711
+ for (attr in attrs) {
712
+ if (!themedContext.theme.resolveAttribute(attr, typedValue, true)) {
713
+ continue
714
+ }
715
+ if (typedValue.resourceId != 0) {
716
+ AppCompatResources.getColorStateList(themedContext, typedValue.resourceId)
717
+ ?.defaultColor
718
+ ?.let { return it }
719
+ } else if (typedValue.type in
720
+ TypedValue.TYPE_FIRST_COLOR_INT..TypedValue.TYPE_LAST_COLOR_INT
721
+ ) {
722
+ return typedValue.data
723
+ }
724
+ }
725
+ return null
726
+ }
727
+
728
+ private fun ensureButtonRipple(button: AppCompatButton, cornerRadiusPx: Float) {
729
+ val existingMask = (button.foreground as? RippleDrawable)
730
+ ?.findDrawableByLayerId(android.R.id.mask) as? GradientDrawable
731
+ if (existingMask != null) {
732
+ existingMask.cornerRadius = cornerRadiusPx
733
+ return
734
+ }
735
+ val mask = GradientDrawable().apply {
736
+ shape = GradientDrawable.RECTANGLE
737
+ cornerRadius = cornerRadiusPx
738
+ setColor(Color.WHITE)
739
+ }
740
+ button.foreground = RippleDrawable(
741
+ ColorStateList.valueOf(resolveColorAttr(android.R.attr.colorControlHighlight)),
742
+ null,
743
+ mask
744
+ )
745
+ }
746
+
747
+ private fun resolveSeparatorColor(): Int = theme?.separatorColor
748
+ ?: theme?.borderColor
749
+ ?: resolveColorAttr(
750
+ MaterialR.attr.colorOutlineVariant,
751
+ MaterialR.attr.colorOutline,
752
+ android.R.attr.textColorHint
753
+ )
754
+
755
+ private fun updateContainerLayout(appearance: EditorToolbarAppearance) {
756
+ val isNative = appearance == EditorToolbarAppearance.NATIVE
757
+ val toolbarHeightDp = resolvedToolbarHeightDp(isNative)
758
+ val buttonSizeDp = resolvedButtonSizeDp(isNative, toolbarHeightDp)
759
+ val horizontalPadding = dp(
760
+ if (isNative) {
761
+ NATIVE_CONTAINER_HORIZONTAL_PADDING_DP
762
+ } else {
763
+ 12
764
+ }
765
+ )
766
+ val verticalPadding =
767
+ dp(resolvedVerticalPaddingDp(isNative, toolbarHeightDp, buttonSizeDp).roundToInt())
768
+ rootRow.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
769
+ rootRow.minimumHeight = dp(toolbarHeightDp.roundToInt())
770
+ startRow.gravity = Gravity.START or Gravity.CENTER_VERTICAL
771
+ contentRow.gravity = Gravity.START or Gravity.CENTER_VERTICAL
772
+ endRow.gravity = Gravity.END or Gravity.CENTER_VERTICAL
773
+ }
774
+
775
+ private fun applyButtonLayout(binding: ButtonBinding, appearance: EditorToolbarAppearance) {
776
+ val button = binding.button
777
+ val isNative = appearance == EditorToolbarAppearance.NATIVE
778
+ val toolbarHeightDp = resolvedToolbarHeightDp(isNative)
779
+ val buttonSizeDp = resolvedButtonSizeDp(isNative, toolbarHeightDp)
780
+ val sizePx = dp(buttonSizeDp.roundToInt())
781
+ val requestedIconSize = binding.item.buttonStyle?.iconSize ?: theme?.buttonIconSize
782
+ button.textSize = requestedIconSize
783
+ ?.takeIf { it.isFinite() && it > 0f }
784
+ ?.coerceAtMost(buttonSizeDp)
785
+ ?: if (isNative) NATIVE_BUTTON_ICON_SIZE_SP else 16f
786
+ button.minWidth = sizePx
787
+ button.minimumWidth = sizePx
788
+ button.minHeight = sizePx
789
+ button.minimumHeight = sizePx
790
+ button.setPadding(
791
+ if (isNative) 0 else dp(10),
792
+ if (isNative) 0 else dp(8),
793
+ if (isNative) 0 else dp(10),
794
+ if (isNative) 0 else dp(8)
795
+ )
796
+ (button.layoutParams as? LinearLayout.LayoutParams)?.let { params ->
797
+ params.marginEnd = dp(if (isNative) NATIVE_ITEM_SPACING_DP else 6)
798
+ button.layoutParams = params
799
+ }
800
+ }
801
+
802
+ private fun configureSeparator(separator: View) {
803
+ val appearance = theme?.appearance ?: EditorToolbarAppearance.CUSTOM
804
+ val params = if (appearance == EditorToolbarAppearance.NATIVE) {
805
+ LinearLayout.LayoutParams(dp(1), dp(24)).apply {
806
+ marginStart = dp(NATIVE_GROUP_SPACING_DP / 2)
807
+ marginEnd = dp(NATIVE_GROUP_SPACING_DP / 2)
808
+ }
809
+ } else {
810
+ LinearLayout.LayoutParams(dp(1), dp(22)).apply {
811
+ marginStart = dp(6)
812
+ marginEnd = dp(6)
813
+ }
814
+ }
815
+ separator.layoutParams = params
816
+ separator.setBackgroundColor(
817
+ if (appearance == EditorToolbarAppearance.NATIVE) {
818
+ withAlpha(resolveSeparatorColor(), 0.6f)
819
+ } else {
820
+ resolveSeparatorColor()
821
+ }
822
+ )
823
+ }
824
+
825
+ private fun resolvedToolbarHeightDp(isNative: Boolean): Float =
826
+ if (isNative) NATIVE_CONTAINER_HEIGHT_DP.toFloat() else (theme?.height ?: 60f)
827
+
828
+ private fun resolvedButtonSizeDp(isNative: Boolean, toolbarHeightDp: Float): Float {
829
+ if (isNative) return NATIVE_BUTTON_SIZE_DP.toFloat()
830
+ if (theme?.height == null) {
831
+ return 36f
832
+ }
833
+ // Sizing contract shared with src/EditorToolbar.tsx (resolvedButtonHeight)
834
+ // and ios/NativeEditorExpoView.swift (resolvedButtonSize): an explicit
835
+ // theme height caps buttons at NATIVE_BUTTON_SIZE_DP regardless of
836
+ // appearance, not the smaller non-native default.
837
+ return maxOf(1f, minOf(NATIVE_BUTTON_SIZE_DP.toFloat(), toolbarHeightDp - 4f))
838
+ }
839
+
840
+ private fun resolvedVerticalPaddingDp(
841
+ isNative: Boolean,
842
+ toolbarHeightDp: Float,
843
+ buttonSizeDp: Float
844
+ ): Float {
845
+ if (isNative) return NATIVE_CONTAINER_VERTICAL_PADDING_DP.toFloat()
846
+ if (theme?.height == null) {
847
+ return 12f
848
+ }
849
+ return maxOf(0f, (toolbarHeightDp - buttonSizeDp) / 2f)
850
+ }
851
+ }