@q9labsai/chalk-react-native 4.1.17 → 5.0.1

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 (544) hide show
  1. package/README.md +134 -29
  2. package/android/build.gradle +35 -0
  3. package/android/compatibility/ModuleApiCompatibility.java +215 -0
  4. package/android/src/main/AndroidManifest.xml +2 -1
  5. package/android/src/main/java/ai/q9labs/chalk/reactnative/ChalkSpaceForegroundService.java +105 -31
  6. package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/ChalkAndroidConnectionServicePackage.kt +2 -0
  7. package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/ChalkAudioRouteModule.kt +137 -0
  8. package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/ChalkCallServiceModule.kt +32 -0
  9. package/android/src/main/kotlin/ai/q9labs/chalk/reactnative/CommunicationDeviceListener.kt +23 -0
  10. package/android/src/test/kotlin/ai/q9labs/chalk/compatibility/UnsafeSignatures.kt +29 -0
  11. package/dist/android/call-service.d.ts +4 -0
  12. package/dist/android/call-service.d.ts.map +1 -0
  13. package/dist/avatar/FaceAvatar.d.ts +19 -0
  14. package/dist/avatar/FaceAvatar.d.ts.map +1 -0
  15. package/dist/avatar/tile-avatar-size.d.ts +3 -0
  16. package/dist/avatar/tile-avatar-size.d.ts.map +1 -0
  17. package/dist/chunk-GYVZSOGQ.js +30 -0
  18. package/dist/chunk-IHE6EA3T.js +25 -0
  19. package/dist/chunk-OIBT2FNM.js +1479 -0
  20. package/dist/client.d.ts +1 -5
  21. package/dist/client.d.ts.map +1 -1
  22. package/dist/client.js +185 -2
  23. package/dist/components/EmbeddedWhiteboard.d.ts.map +1 -1
  24. package/dist/components/native-media-visibility.d.ts +3 -2
  25. package/dist/components/native-media-visibility.d.ts.map +1 -1
  26. package/dist/components/useAndroidCallService.d.ts +8 -0
  27. package/dist/components/useAndroidCallService.d.ts.map +1 -0
  28. package/dist/dev.d.ts +10 -0
  29. package/dist/dev.d.ts.map +1 -0
  30. package/dist/dev.js +10 -0
  31. package/dist/dock/Dock.d.ts +23 -0
  32. package/dist/dock/Dock.d.ts.map +1 -0
  33. package/dist/dock/LeaveMenu.d.ts +12 -0
  34. package/dist/dock/LeaveMenu.d.ts.map +1 -0
  35. package/dist/dock/MoreSheet.d.ts +17 -0
  36. package/dist/dock/MoreSheet.d.ts.map +1 -0
  37. package/dist/dock/ReactionBurst.d.ts +8 -0
  38. package/dist/dock/ReactionBurst.d.ts.map +1 -0
  39. package/dist/dock/RoutePicker.d.ts +16 -0
  40. package/dist/dock/RoutePicker.d.ts.map +1 -0
  41. package/dist/dock/audio-route.d.ts +25 -0
  42. package/dist/dock/audio-route.d.ts.map +1 -0
  43. package/dist/error-YOPTEH5N.wav +0 -0
  44. package/dist/frame/CallFrame.d.ts +17 -0
  45. package/dist/frame/CallFrame.d.ts.map +1 -0
  46. package/dist/frame/Chalk.d.ts +40 -0
  47. package/dist/frame/Chalk.d.ts.map +1 -0
  48. package/dist/frame/back-handler.d.ts +6 -0
  49. package/dist/frame/back-handler.d.ts.map +1 -0
  50. package/dist/frame/dev-scenario.d.ts +28 -0
  51. package/dist/frame/dev-scenario.d.ts.map +1 -0
  52. package/dist/frame/device-layout.d.ts +16 -0
  53. package/dist/frame/device-layout.d.ts.map +1 -0
  54. package/dist/frame/measurement.d.ts +28 -0
  55. package/dist/frame/measurement.d.ts.map +1 -0
  56. package/dist/frame/portal.d.ts +24 -0
  57. package/dist/frame/portal.d.ts.map +1 -0
  58. package/dist/frame/preferences.d.ts +21 -0
  59. package/dist/frame/preferences.d.ts.map +1 -0
  60. package/dist/frame/regions.d.ts +25 -0
  61. package/dist/frame/regions.d.ts.map +1 -0
  62. package/dist/hand-DFWVXPH2.wav +0 -0
  63. package/dist/haptics/haptics.d.ts +12 -0
  64. package/dist/haptics/haptics.d.ts.map +1 -0
  65. package/dist/header/CallDetailsSheet.d.ts +18 -0
  66. package/dist/header/CallDetailsSheet.d.ts.map +1 -0
  67. package/dist/header/Header.d.ts +17 -0
  68. package/dist/header/Header.d.ts.map +1 -0
  69. package/dist/header/QrCode.d.ts +6 -0
  70. package/dist/header/QrCode.d.ts.map +1 -0
  71. package/dist/icons/Icon.d.ts +12 -0
  72. package/dist/icons/Icon.d.ts.map +1 -0
  73. package/dist/icons/icon-element.d.ts +24 -0
  74. package/dist/icons/icon-element.d.ts.map +1 -0
  75. package/dist/icons/icon-paths.d.ts +530 -0
  76. package/dist/icons/icon-paths.d.ts.map +1 -0
  77. package/dist/index.d.ts +6 -9
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +2956 -38
  80. package/dist/join-Z6RL5AHE.wav +0 -0
  81. package/dist/knock-4VBQSHK4.wav +0 -0
  82. package/dist/leave-MQLZOWQD.wav +0 -0
  83. package/dist/limbo/Limbo.d.ts +25 -0
  84. package/dist/limbo/Limbo.d.ts.map +1 -0
  85. package/dist/limbo/Veil.d.ts +10 -0
  86. package/dist/limbo/Veil.d.ts.map +1 -0
  87. package/dist/media/native-capture-error.d.ts +7 -0
  88. package/dist/media/native-capture-error.d.ts.map +1 -0
  89. package/dist/message-6Y6WJGTB.wav +0 -0
  90. package/dist/motion/MovingFrame.d.ts +36 -0
  91. package/dist/motion/MovingFrame.d.ts.map +1 -0
  92. package/dist/motion/motion.d.ts +82 -0
  93. package/dist/motion/motion.d.ts.map +1 -0
  94. package/dist/motion/presence.d.ts +6 -0
  95. package/dist/motion/presence.d.ts.map +1 -0
  96. package/dist/motion/press.d.ts +15 -0
  97. package/dist/motion/press.d.ts.map +1 -0
  98. package/dist/mute-H5SJFXVI.wav +0 -0
  99. package/dist/primitives/BottomSheet.d.ts +36 -0
  100. package/dist/primitives/BottomSheet.d.ts.map +1 -0
  101. package/dist/primitives/ChalkText.d.ts +13 -0
  102. package/dist/primitives/ChalkText.d.ts.map +1 -0
  103. package/dist/primitives/CopyButton.d.ts +11 -0
  104. package/dist/primitives/CopyButton.d.ts.map +1 -0
  105. package/dist/primitives/IconButton.d.ts +26 -0
  106. package/dist/primitives/IconButton.d.ts.map +1 -0
  107. package/dist/primitives/Menu.d.ts +39 -0
  108. package/dist/primitives/Menu.d.ts.map +1 -0
  109. package/dist/primitives/NameTag.d.ts +19 -0
  110. package/dist/primitives/NameTag.d.ts.map +1 -0
  111. package/dist/primitives/PillButton.d.ts +19 -0
  112. package/dist/primitives/PillButton.d.ts.map +1 -0
  113. package/dist/primitives/SegmentedControl.d.ts +17 -0
  114. package/dist/primitives/SegmentedControl.d.ts.map +1 -0
  115. package/dist/primitives/SheetGroup.d.ts +24 -0
  116. package/dist/primitives/SheetGroup.d.ts.map +1 -0
  117. package/dist/primitives/Switch.d.ts +9 -0
  118. package/dist/primitives/Switch.d.ts.map +1 -0
  119. package/dist/primitives/TileShell.d.ts +18 -0
  120. package/dist/primitives/TileShell.d.ts.map +1 -0
  121. package/dist/primitives/announce.d.ts +3 -0
  122. package/dist/primitives/announce.d.ts.map +1 -0
  123. package/dist/primitives/menu-placement.d.ts +32 -0
  124. package/dist/primitives/menu-placement.d.ts.map +1 -0
  125. package/dist/primitives/toast/Toast.d.ts +8 -0
  126. package/dist/primitives/toast/Toast.d.ts.map +1 -0
  127. package/dist/primitives/toast/ToastStack.d.ts +18 -0
  128. package/dist/primitives/toast/ToastStack.d.ts.map +1 -0
  129. package/dist/primitives/toast/toast-store.d.ts +39 -0
  130. package/dist/primitives/toast/toast-store.d.ts.map +1 -0
  131. package/dist/recording-Q4GB77Y4.wav +0 -0
  132. package/dist/sheets/people-chat/ChatNotices.d.ts +14 -0
  133. package/dist/sheets/people-chat/ChatNotices.d.ts.map +1 -0
  134. package/dist/sheets/people-chat/ChatTab.d.ts +6 -0
  135. package/dist/sheets/people-chat/ChatTab.d.ts.map +1 -0
  136. package/dist/sheets/people-chat/Composer.d.ts +7 -0
  137. package/dist/sheets/people-chat/Composer.d.ts.map +1 -0
  138. package/dist/sheets/people-chat/PeopleChatSheet.d.ts +17 -0
  139. package/dist/sheets/people-chat/PeopleChatSheet.d.ts.map +1 -0
  140. package/dist/sheets/people-chat/PeopleTab.d.ts +3 -0
  141. package/dist/sheets/people-chat/PeopleTab.d.ts.map +1 -0
  142. package/dist/sheets/people-chat/PhotoViewer.d.ts +19 -0
  143. package/dist/sheets/people-chat/PhotoViewer.d.ts.map +1 -0
  144. package/dist/sheets/people-chat/TextField.d.ts +4 -0
  145. package/dist/sheets/people-chat/TextField.d.ts.map +1 -0
  146. package/dist/sheets/people-chat/attachments.d.ts +33 -0
  147. package/dist/sheets/people-chat/attachments.d.ts.map +1 -0
  148. package/dist/sheets/people-chat/chat-model.d.ts +64 -0
  149. package/dist/sheets/people-chat/chat-model.d.ts.map +1 -0
  150. package/dist/sheets/people-chat/people-model.d.ts +43 -0
  151. package/dist/sheets/people-chat/people-model.d.ts.map +1 -0
  152. package/dist/sounds/sounds.d.ts +33 -0
  153. package/dist/sounds/sounds.d.ts.map +1 -0
  154. package/dist/space-client/create-native-space-client.d.ts +0 -4
  155. package/dist/space-client/create-native-space-client.d.ts.map +1 -1
  156. package/dist/space-client/random-uuid.d.ts +7 -0
  157. package/dist/space-client/random-uuid.d.ts.map +1 -0
  158. package/dist/stage/ContentTile.d.ts +18 -0
  159. package/dist/stage/ContentTile.d.ts.map +1 -0
  160. package/dist/stage/ParticipantTile.d.ts +18 -0
  161. package/dist/stage/ParticipantTile.d.ts.map +1 -0
  162. package/dist/stage/Stage.d.ts +14 -0
  163. package/dist/stage/Stage.d.ts.map +1 -0
  164. package/dist/stage/TileSheet.d.ts +12 -0
  165. package/dist/stage/TileSheet.d.ts.map +1 -0
  166. package/dist/stage/camera.d.ts +7 -0
  167. package/dist/stage/camera.d.ts.map +1 -0
  168. package/dist/stage/layout.d.ts +57 -0
  169. package/dist/stage/layout.d.ts.map +1 -0
  170. package/dist/stage/space-selector.d.ts +10 -0
  171. package/dist/stage/space-selector.d.ts.map +1 -0
  172. package/dist/stage/stage-view.d.ts +23 -0
  173. package/dist/stage/stage-view.d.ts.map +1 -0
  174. package/dist/status/CallOverlays.d.ts +17 -0
  175. package/dist/status/CallOverlays.d.ts.map +1 -0
  176. package/dist/status/ChalkArrival.d.ts +24 -0
  177. package/dist/status/ChalkArrival.d.ts.map +1 -0
  178. package/dist/status/StatusPage.d.ts +14 -0
  179. package/dist/status/StatusPage.d.ts.map +1 -0
  180. package/dist/status/call-screen.d.ts +20 -0
  181. package/dist/status/call-screen.d.ts.map +1 -0
  182. package/dist/test-VD2KAM4D.wav +0 -0
  183. package/dist/theme/theme.d.ts +15 -0
  184. package/dist/theme/theme.d.ts.map +1 -0
  185. package/dist/theme/tokens.d.ts +248 -0
  186. package/dist/theme/tokens.d.ts.map +1 -0
  187. package/dist/toasts/CallEventToasts.d.ts +21 -0
  188. package/dist/toasts/CallEventToasts.d.ts.map +1 -0
  189. package/dist/toasts/notices.d.ts +34 -0
  190. package/dist/toasts/notices.d.ts.map +1 -0
  191. package/dist/ui/ChalkUI.d.ts +12 -0
  192. package/dist/ui/ChalkUI.d.ts.map +1 -0
  193. package/dist/ui.d.ts +25 -0
  194. package/dist/ui.d.ts.map +1 -0
  195. package/dist/ui.js +77 -0
  196. package/dist/unmute-2XR2LICU.wav +0 -0
  197. package/embedded/chalk-whiteboard/asset-manifest.json +2 -2
  198. package/embedded/chalk-whiteboard/renderer.js +510 -510
  199. package/ios/ChalkAudioRouteModule.swift +93 -0
  200. package/ios/ChalkAudioRouteModuleBridge.m +9 -0
  201. package/package.json +46 -76
  202. package/src/android/call-service.ts +19 -0
  203. package/src/avatar/FaceAvatar.tsx +145 -0
  204. package/src/avatar/tile-avatar-size.ts +4 -0
  205. package/src/client.ts +1 -5
  206. package/src/components/EmbeddedWhiteboard.tsx +14 -71
  207. package/src/components/native-media-visibility.ts +1 -2
  208. package/src/components/useAndroidCallService.ts +41 -0
  209. package/src/dev.ts +9 -0
  210. package/src/dock/Dock.tsx +95 -0
  211. package/src/dock/LeaveMenu.tsx +21 -0
  212. package/src/dock/MoreSheet.tsx +233 -0
  213. package/src/dock/ReactionBurst.tsx +55 -0
  214. package/src/dock/RoutePicker.tsx +42 -0
  215. package/src/dock/audio-route.ts +69 -0
  216. package/src/frame/CallFrame.tsx +209 -0
  217. package/src/frame/Chalk.tsx +83 -0
  218. package/src/frame/back-handler.ts +19 -0
  219. package/src/frame/dev-scenario.ts +48 -0
  220. package/src/frame/device-layout.tsx +31 -0
  221. package/src/frame/measurement.ts +44 -0
  222. package/src/frame/portal.tsx +107 -0
  223. package/src/frame/preferences.tsx +48 -0
  224. package/src/frame/regions.test.ts +38 -0
  225. package/src/frame/regions.ts +60 -0
  226. package/src/haptics/haptics.ts +19 -0
  227. package/src/header/CallDetailsSheet.tsx +139 -0
  228. package/src/header/Header.tsx +107 -0
  229. package/src/header/QrCode.tsx +34 -0
  230. package/src/icons/Icon.tsx +35 -0
  231. package/src/icons/icon-element.ts +11 -0
  232. package/src/icons/icon-paths.ts +199 -0
  233. package/src/index.ts +6 -9
  234. package/src/limbo/Limbo.tsx +159 -0
  235. package/src/limbo/Veil.tsx +36 -0
  236. package/src/media/native-capture-error.test.ts +19 -0
  237. package/src/media/native-capture-error.ts +13 -0
  238. package/src/motion/MovingFrame.tsx +101 -0
  239. package/src/motion/motion.ts +33 -0
  240. package/src/motion/presence.ts +20 -0
  241. package/src/motion/press.ts +35 -0
  242. package/src/primitives/BottomSheet.tsx +211 -0
  243. package/src/primitives/ChalkText.tsx +23 -0
  244. package/src/primitives/CopyButton.tsx +78 -0
  245. package/src/primitives/IconButton.tsx +90 -0
  246. package/src/primitives/Menu.tsx +212 -0
  247. package/src/primitives/NameTag.tsx +68 -0
  248. package/src/primitives/PillButton.tsx +56 -0
  249. package/src/primitives/SegmentedControl.tsx +55 -0
  250. package/src/primitives/SheetGroup.tsx +72 -0
  251. package/src/primitives/Switch.tsx +39 -0
  252. package/src/primitives/TileShell.tsx +53 -0
  253. package/src/primitives/announce.ts +7 -0
  254. package/src/primitives/menu-placement.test.ts +50 -0
  255. package/src/primitives/menu-placement.ts +43 -0
  256. package/src/primitives/toast/Toast.tsx +50 -0
  257. package/src/primitives/toast/ToastStack.tsx +89 -0
  258. package/src/primitives/toast/toast-store.ts +86 -0
  259. package/src/sheets/people-chat/ChatNotices.tsx +69 -0
  260. package/src/sheets/people-chat/ChatTab.tsx +186 -0
  261. package/src/sheets/people-chat/Composer.tsx +182 -0
  262. package/src/sheets/people-chat/PeopleChatSheet.tsx +100 -0
  263. package/src/sheets/people-chat/PeopleTab.tsx +326 -0
  264. package/src/sheets/people-chat/PhotoViewer.tsx +132 -0
  265. package/src/sheets/people-chat/TextField.tsx +14 -0
  266. package/src/sheets/people-chat/attachments.tsx +183 -0
  267. package/src/sheets/people-chat/chat-model.ts +157 -0
  268. package/src/sheets/people-chat/people-chat-model.test.ts +98 -0
  269. package/src/sheets/people-chat/people-model.ts +68 -0
  270. package/src/sounds/assets/error.wav +0 -0
  271. package/src/sounds/assets/hand.wav +0 -0
  272. package/src/sounds/assets/join.wav +0 -0
  273. package/src/sounds/assets/knock.wav +0 -0
  274. package/src/sounds/assets/leave.wav +0 -0
  275. package/src/sounds/assets/message.wav +0 -0
  276. package/src/sounds/assets/mute.wav +0 -0
  277. package/src/sounds/assets/recording.wav +0 -0
  278. package/src/sounds/assets/test.wav +0 -0
  279. package/src/sounds/assets/unmute.wav +0 -0
  280. package/src/sounds/assets.d.ts +5 -0
  281. package/src/sounds/sounds.ts +50 -0
  282. package/src/space-client/create-native-space-client.ts +20 -181
  283. package/src/space-client/random-uuid.ts +17 -0
  284. package/src/space-client/whiteboard-watchdog.test.ts +23 -0
  285. package/src/stage/ContentTile.tsx +100 -0
  286. package/src/stage/ParticipantTile.tsx +140 -0
  287. package/src/stage/Stage.tsx +152 -0
  288. package/src/stage/TileSheet.tsx +116 -0
  289. package/src/stage/camera.ts +20 -0
  290. package/src/stage/layout.test.ts +77 -0
  291. package/src/stage/layout.ts +226 -0
  292. package/src/stage/space-selector.ts +31 -0
  293. package/src/stage/stage-view.tsx +68 -0
  294. package/src/status/CallOverlays.tsx +47 -0
  295. package/src/status/ChalkArrival.tsx +53 -0
  296. package/src/status/StatusPage.tsx +123 -0
  297. package/src/status/call-screen.test.ts +42 -0
  298. package/src/status/call-screen.ts +29 -0
  299. package/src/theme/theme.tsx +28 -0
  300. package/src/theme/tokens.ts +247 -0
  301. package/src/toasts/CallEventToasts.tsx +192 -0
  302. package/src/toasts/notices.test.ts +92 -0
  303. package/src/toasts/notices.ts +62 -0
  304. package/src/ui/ChalkUI.tsx +49 -0
  305. package/src/ui.ts +24 -0
  306. package/dist/android.d.ts +0 -2
  307. package/dist/android.d.ts.map +0 -1
  308. package/dist/android.js +0 -62
  309. package/dist/chunk-2OS7SSXE.js +0 -552
  310. package/dist/chunk-5DQ4KBRH.js +0 -690
  311. package/dist/chunk-ITLUHTVS.js +0 -5271
  312. package/dist/chunk-JB4LNWVP.js +0 -247
  313. package/dist/components/Chalk.d.ts +0 -79
  314. package/dist/components/Chalk.d.ts.map +0 -1
  315. package/dist/components/ChatMessageList.d.ts +0 -11
  316. package/dist/components/ChatMessageList.d.ts.map +0 -1
  317. package/dist/components/Entrance.d.ts +0 -22
  318. package/dist/components/Entrance.d.ts.map +0 -1
  319. package/dist/components/EntranceView.android.d.ts +0 -4
  320. package/dist/components/EntranceView.android.d.ts.map +0 -1
  321. package/dist/components/EntranceView.d.ts +0 -18
  322. package/dist/components/EntranceView.d.ts.map +0 -1
  323. package/dist/components/EntranceView.ios-pad.d.ts +0 -3
  324. package/dist/components/EntranceView.ios-pad.d.ts.map +0 -1
  325. package/dist/components/EntranceView.ios-phone.d.ts +0 -3
  326. package/dist/components/EntranceView.ios-phone.d.ts.map +0 -1
  327. package/dist/components/EntranceView.macos.d.ts +0 -4
  328. package/dist/components/EntranceView.macos.d.ts.map +0 -1
  329. package/dist/components/FaceAvatar.d.ts +0 -16
  330. package/dist/components/FaceAvatar.d.ts.map +0 -1
  331. package/dist/components/GradientSurface.d.ts +0 -14
  332. package/dist/components/GradientSurface.d.ts.map +0 -1
  333. package/dist/components/MediaView.d.ts +0 -14
  334. package/dist/components/MediaView.d.ts.map +0 -1
  335. package/dist/components/ReactionPicker.d.ts +0 -10
  336. package/dist/components/ReactionPicker.d.ts.map +0 -1
  337. package/dist/components/SpaceView.android.d.ts +0 -2
  338. package/dist/components/SpaceView.android.d.ts.map +0 -1
  339. package/dist/components/SpaceView.d.ts +0 -33
  340. package/dist/components/SpaceView.d.ts.map +0 -1
  341. package/dist/components/SpaceView.ios-pad.d.ts +0 -2
  342. package/dist/components/SpaceView.ios-pad.d.ts.map +0 -1
  343. package/dist/components/SpaceView.ios-phone.d.ts +0 -2
  344. package/dist/components/SpaceView.ios-phone.d.ts.map +0 -1
  345. package/dist/components/SpaceView.macos.d.ts +0 -2
  346. package/dist/components/SpaceView.macos.d.ts.map +0 -1
  347. package/dist/components/SpaceView.shared.d.ts +0 -3
  348. package/dist/components/SpaceView.shared.d.ts.map +0 -1
  349. package/dist/components/episode-diagnostic-render-observer.d.ts +0 -2
  350. package/dist/components/episode-diagnostic-render-observer.d.ts.map +0 -1
  351. package/dist/components/logo/Logo.d.ts +0 -11
  352. package/dist/components/logo/Logo.d.ts.map +0 -1
  353. package/dist/components/native-animation-controller.d.ts +0 -7
  354. package/dist/components/native-animation-controller.d.ts.map +0 -1
  355. package/dist/components/native-chat.d.ts +0 -8
  356. package/dist/components/native-chat.d.ts.map +0 -1
  357. package/dist/components/native-entrance/entrance-controller-store.d.ts +0 -34
  358. package/dist/components/native-entrance/entrance-controller-store.d.ts.map +0 -1
  359. package/dist/components/native-entrance/useEntranceController.d.ts +0 -19
  360. package/dist/components/native-entrance/useEntranceController.d.ts.map +0 -1
  361. package/dist/components/native-joining-loading-animation.d.ts +0 -9
  362. package/dist/components/native-joining-loading-animation.d.ts.map +0 -1
  363. package/dist/components/native-space-view/AppearanceSettings.d.ts +0 -2
  364. package/dist/components/native-space-view/AppearanceSettings.d.ts.map +0 -1
  365. package/dist/components/native-space-view/FeedbackSheet.d.ts +0 -12
  366. package/dist/components/native-space-view/FeedbackSheet.d.ts.map +0 -1
  367. package/dist/components/native-space-view/ParticipantActionSheet.d.ts +0 -9
  368. package/dist/components/native-space-view/ParticipantActionSheet.d.ts.map +0 -1
  369. package/dist/components/native-space-view/SettingsSheet.d.ts +0 -7
  370. package/dist/components/native-space-view/SettingsSheet.d.ts.map +0 -1
  371. package/dist/components/native-space-view/SpaceActionMenu.d.ts +0 -10
  372. package/dist/components/native-space-view/SpaceActionMenu.d.ts.map +0 -1
  373. package/dist/components/native-space-view/SpaceBottomDock.android.d.ts +0 -4
  374. package/dist/components/native-space-view/SpaceBottomDock.android.d.ts.map +0 -1
  375. package/dist/components/native-space-view/SpaceChatAttachment.d.ts +0 -8
  376. package/dist/components/native-space-view/SpaceChatAttachment.d.ts.map +0 -1
  377. package/dist/components/native-space-view/SpaceChatSheet.d.ts +0 -5
  378. package/dist/components/native-space-view/SpaceChatSheet.d.ts.map +0 -1
  379. package/dist/components/native-space-view/SpaceGrid.android.d.ts +0 -8
  380. package/dist/components/native-space-view/SpaceGrid.android.d.ts.map +0 -1
  381. package/dist/components/native-space-view/SpaceMoreSheet.d.ts +0 -7
  382. package/dist/components/native-space-view/SpaceMoreSheet.d.ts.map +0 -1
  383. package/dist/components/native-space-view/SpacePanelSheet.d.ts +0 -5
  384. package/dist/components/native-space-view/SpacePanelSheet.d.ts.map +0 -1
  385. package/dist/components/native-space-view/SpacePeopleSheet.d.ts +0 -5
  386. package/dist/components/native-space-view/SpacePeopleSheet.d.ts.map +0 -1
  387. package/dist/components/native-space-view/SpaceStage.android.d.ts +0 -29
  388. package/dist/components/native-space-view/SpaceStage.android.d.ts.map +0 -1
  389. package/dist/components/native-space-view/SpaceSurfacePrimitives.d.ts +0 -30
  390. package/dist/components/native-space-view/SpaceSurfacePrimitives.d.ts.map +0 -1
  391. package/dist/components/native-space-view/SpaceTopBar.android.d.ts +0 -11
  392. package/dist/components/native-space-view/SpaceTopBar.android.d.ts.map +0 -1
  393. package/dist/components/native-space-view/SpaceWhiteboardSurface.d.ts +0 -15
  394. package/dist/components/native-space-view/SpaceWhiteboardSurface.d.ts.map +0 -1
  395. package/dist/components/native-space-view/diagnostics.d.ts +0 -48
  396. package/dist/components/native-space-view/diagnostics.d.ts.map +0 -1
  397. package/dist/components/native-space-view/format-space-duration.d.ts +0 -2
  398. package/dist/components/native-space-view/format-space-duration.d.ts.map +0 -1
  399. package/dist/components/native-space-view/native-space-grid-keys.d.ts +0 -3
  400. package/dist/components/native-space-view/native-space-grid-keys.d.ts.map +0 -1
  401. package/dist/components/native-space-view/native-space-page-store.d.ts +0 -8
  402. package/dist/components/native-space-view/native-space-page-store.d.ts.map +0 -1
  403. package/dist/components/native-space-view/space-chat-attachments.d.ts +0 -15
  404. package/dist/components/native-space-view/space-chat-attachments.d.ts.map +0 -1
  405. package/dist/components/native-space-view/space-panel-layout.d.ts +0 -10
  406. package/dist/components/native-space-view/space-panel-layout.d.ts.map +0 -1
  407. package/dist/components/native-space-view/space-progressive-surface-helpers.d.ts +0 -16
  408. package/dist/components/native-space-view/space-progressive-surface-helpers.d.ts.map +0 -1
  409. package/dist/components/native-space-view/space-progressive-surface-types.d.ts +0 -3
  410. package/dist/components/native-space-view/space-progressive-surface-types.d.ts.map +0 -1
  411. package/dist/components/native-space-view/types.d.ts +0 -24
  412. package/dist/components/native-space-view/types.d.ts.map +0 -1
  413. package/dist/components/native-space-view/useSpaceViewController.contract.d.ts +0 -41
  414. package/dist/components/native-space-view/useSpaceViewController.contract.d.ts.map +0 -1
  415. package/dist/components/native-space-view/useSpaceViewController.d.ts +0 -156
  416. package/dist/components/native-space-view/useSpaceViewController.d.ts.map +0 -1
  417. package/dist/components/native-space-view/useSpaceViewDerived.d.ts +0 -12
  418. package/dist/components/native-space-view/useSpaceViewDerived.d.ts.map +0 -1
  419. package/dist/components/native-space-view/useSpaceViewPanels.d.ts +0 -70
  420. package/dist/components/native-space-view/useSpaceViewPanels.d.ts.map +0 -1
  421. package/dist/feedback/native-capture.d.ts +0 -9
  422. package/dist/feedback/native-capture.d.ts.map +0 -1
  423. package/dist/feedback/native-evidence.d.ts +0 -3
  424. package/dist/feedback/native-evidence.d.ts.map +0 -1
  425. package/dist/hooks/entrance-preview-store.d.ts +0 -16
  426. package/dist/hooks/entrance-preview-store.d.ts.map +0 -1
  427. package/dist/hooks/useEntrancePreview.d.ts +0 -7
  428. package/dist/hooks/useEntrancePreview.d.ts.map +0 -1
  429. package/dist/hooks/useLayout.d.ts +0 -16
  430. package/dist/hooks/useLayout.d.ts.map +0 -1
  431. package/dist/hooks/usePanels.d.ts +0 -12
  432. package/dist/hooks/usePanels.d.ts.map +0 -1
  433. package/dist/ios-pad.d.ts +0 -2
  434. package/dist/ios-pad.d.ts.map +0 -1
  435. package/dist/ios-pad.js +0 -62
  436. package/dist/ios-phone.d.ts +0 -2
  437. package/dist/ios-phone.d.ts.map +0 -1
  438. package/dist/ios-phone.js +0 -62
  439. package/dist/ios.d.ts +0 -2
  440. package/dist/ios.d.ts.map +0 -1
  441. package/dist/ios.js +0 -62
  442. package/dist/macos.d.ts +0 -2
  443. package/dist/macos.d.ts.map +0 -1
  444. package/dist/macos.js +0 -62
  445. package/dist/space-client/cloudflare-rtk-native.d.ts +0 -3
  446. package/dist/space-client/cloudflare-rtk-native.d.ts.map +0 -1
  447. package/dist/telemetry.d.ts +0 -63
  448. package/dist/telemetry.d.ts.map +0 -1
  449. package/dist/ui/appearance.d.ts +0 -246
  450. package/dist/ui/appearance.d.ts.map +0 -1
  451. package/dist/ui/appearance.js +0 -14
  452. package/dist/ui/native-appearance-context.d.ts +0 -15
  453. package/dist/ui/native-appearance-context.d.ts.map +0 -1
  454. package/dist/ui/native-theme.d.ts +0 -17
  455. package/dist/ui/native-theme.d.ts.map +0 -1
  456. package/dist/ui/native-types.d.ts +0 -38
  457. package/dist/ui/native-types.d.ts.map +0 -1
  458. package/dist/ui/participant-avatar.d.ts +0 -21
  459. package/dist/ui/participant-avatar.d.ts.map +0 -1
  460. package/dist/ui/theme-tokens.d.ts +0 -25
  461. package/dist/ui/theme-tokens.d.ts.map +0 -1
  462. package/dist/ui/theme.d.ts +0 -404
  463. package/dist/ui/theme.d.ts.map +0 -1
  464. package/dist/ui/theme.js +0 -8
  465. package/dist/utils/native-space-layout.d.ts +0 -42
  466. package/dist/utils/native-space-layout.d.ts.map +0 -1
  467. package/src/android.ts +0 -1
  468. package/src/components/Chalk.tsx +0 -363
  469. package/src/components/ChatMessageList.tsx +0 -209
  470. package/src/components/Entrance.tsx +0 -42
  471. package/src/components/EntranceView.android.tsx +0 -274
  472. package/src/components/EntranceView.ios-pad.tsx +0 -326
  473. package/src/components/EntranceView.ios-phone.tsx +0 -271
  474. package/src/components/EntranceView.macos.tsx +0 -278
  475. package/src/components/EntranceView.tsx +0 -38
  476. package/src/components/FaceAvatar.tsx +0 -120
  477. package/src/components/GradientSurface.tsx +0 -23
  478. package/src/components/MediaView.tsx +0 -76
  479. package/src/components/ReactionPicker.tsx +0 -98
  480. package/src/components/SpaceView.android.tsx +0 -1
  481. package/src/components/SpaceView.ios-pad.tsx +0 -1
  482. package/src/components/SpaceView.ios-phone.tsx +0 -1
  483. package/src/components/SpaceView.macos.tsx +0 -1
  484. package/src/components/SpaceView.shared.tsx +0 -98
  485. package/src/components/SpaceView.tsx +0 -54
  486. package/src/components/episode-diagnostic-render-observer.ts +0 -12
  487. package/src/components/logo/Logo.tsx +0 -309
  488. package/src/components/native-animation-controller.ts +0 -36
  489. package/src/components/native-chat.ts +0 -23
  490. package/src/components/native-entrance/entrance-controller-store.ts +0 -112
  491. package/src/components/native-entrance/useEntranceController.ts +0 -60
  492. package/src/components/native-joining-loading-animation.ts +0 -26
  493. package/src/components/native-space-view/AppearanceSettings.tsx +0 -114
  494. package/src/components/native-space-view/FeedbackSheet.tsx +0 -296
  495. package/src/components/native-space-view/ParticipantActionSheet.tsx +0 -99
  496. package/src/components/native-space-view/SettingsSheet.tsx +0 -164
  497. package/src/components/native-space-view/SpaceActionMenu.tsx +0 -109
  498. package/src/components/native-space-view/SpaceBottomDock.android.tsx +0 -115
  499. package/src/components/native-space-view/SpaceChatAttachment.tsx +0 -105
  500. package/src/components/native-space-view/SpaceChatSheet.tsx +0 -184
  501. package/src/components/native-space-view/SpaceGrid.android.tsx +0 -323
  502. package/src/components/native-space-view/SpaceMoreSheet.tsx +0 -190
  503. package/src/components/native-space-view/SpacePanelSheet.tsx +0 -45
  504. package/src/components/native-space-view/SpacePeopleSheet.tsx +0 -116
  505. package/src/components/native-space-view/SpaceStage.android.tsx +0 -372
  506. package/src/components/native-space-view/SpaceSurfacePrimitives.tsx +0 -75
  507. package/src/components/native-space-view/SpaceTopBar.android.tsx +0 -113
  508. package/src/components/native-space-view/SpaceWhiteboardSurface.tsx +0 -69
  509. package/src/components/native-space-view/diagnostics.ts +0 -84
  510. package/src/components/native-space-view/format-space-duration.ts +0 -3
  511. package/src/components/native-space-view/native-space-grid-keys.ts +0 -6
  512. package/src/components/native-space-view/native-space-page-store.ts +0 -41
  513. package/src/components/native-space-view/space-chat-attachments.test.ts +0 -26
  514. package/src/components/native-space-view/space-chat-attachments.ts +0 -37
  515. package/src/components/native-space-view/space-panel-layout.ts +0 -10
  516. package/src/components/native-space-view/space-progressive-surface-helpers.ts +0 -33
  517. package/src/components/native-space-view/space-progressive-surface-types.ts +0 -3
  518. package/src/components/native-space-view/types.ts +0 -25
  519. package/src/components/native-space-view/useSpaceViewController.contract.ts +0 -34
  520. package/src/components/native-space-view/useSpaceViewController.ts +0 -416
  521. package/src/components/native-space-view/useSpaceViewDerived.ts +0 -29
  522. package/src/components/native-space-view/useSpaceViewPanels.ts +0 -170
  523. package/src/feedback/native-capture.ts +0 -62
  524. package/src/feedback/native-evidence.ts +0 -37
  525. package/src/hooks/entrance-preview-store.ts +0 -99
  526. package/src/hooks/useEntrancePreview.ts +0 -26
  527. package/src/hooks/useLayout.ts +0 -37
  528. package/src/hooks/usePanels.ts +0 -30
  529. package/src/ios-pad.ts +0 -1
  530. package/src/ios-phone.ts +0 -1
  531. package/src/ios.ts +0 -1
  532. package/src/macos.ts +0 -1
  533. package/src/space-client/cloudflare-rtk-native.ts +0 -104
  534. package/src/telemetry.ts +0 -234
  535. package/src/types/hugeicons-direct.d.ts +0 -4
  536. package/src/types/react-native-safe-area-context.d.ts +0 -8
  537. package/src/ui/appearance.ts +0 -808
  538. package/src/ui/native-appearance-context.tsx +0 -74
  539. package/src/ui/native-theme.tsx +0 -54
  540. package/src/ui/native-types.ts +0 -40
  541. package/src/ui/participant-avatar.ts +0 -47
  542. package/src/ui/theme-tokens.ts +0 -41
  543. package/src/ui/theme.ts +0 -214
  544. package/src/utils/native-space-layout.ts +0 -135
@@ -0,0 +1,78 @@
1
+ import { memo, useCallback, useEffect, useRef, useState } from "react";
2
+ import { Pressable, StyleSheet } from "react-native";
3
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
4
+ import { Icon } from "../icons/Icon";
5
+ import { timings } from "../motion/motion";
6
+ import { usePressAnimation } from "../motion/press";
7
+ import { useTheme } from "../theme/theme";
8
+ import { durations, fixed } from "../theme/tokens";
9
+ import { announce } from "./announce";
10
+
11
+ export type CopyButtonProps = {
12
+ /** Writes the value to the clipboard. The button morphs to a check once it resolves. */
13
+ readonly onCopy: () => void | Promise<void>;
14
+ readonly accessibilityLabel: string;
15
+ /** Spoken once the copy succeeds, for example "Link copied". */
16
+ readonly announcement: string;
17
+ readonly testID?: string;
18
+ };
19
+
20
+ /** The mockup's copy button (`.cp`): the copy icon turns into a green check for two seconds. */
21
+ export const CopyButton = memo(function CopyButton({ onCopy, accessibilityLabel, announcement, testID }: CopyButtonProps): React.JSX.Element {
22
+ const { colors } = useTheme();
23
+ const press = usePressAnimation("small");
24
+ const [copied, setCopied] = useState(false);
25
+ const done = useSharedValue(0);
26
+ const reset = useRef<ReturnType<typeof setTimeout> | null>(null);
27
+
28
+ useEffect(() => {
29
+ done.value = withTiming(copied ? 1 : 0, timings.copyMorph);
30
+ }, [copied, done]);
31
+ useEffect(() => () => clearReset(reset), []);
32
+
33
+ const copy = useCallback(async () => {
34
+ await onCopy();
35
+ setCopied(true);
36
+ announce(announcement);
37
+ clearReset(reset);
38
+ reset.current = setTimeout(() => setCopied(false), durations.copyReset);
39
+ }, [announcement, onCopy]);
40
+
41
+ const copyIconStyle = useAnimatedStyle(() => {
42
+ "worklet";
43
+ return { opacity: 1 - done.value, transform: [{ scale: 1 - 0.6 * done.value }, { rotate: `${20 * done.value}deg` }] };
44
+ });
45
+ const checkIconStyle = useAnimatedStyle(() => {
46
+ "worklet";
47
+ return { opacity: done.value, transform: [{ scale: 0.4 + 0.6 * done.value }, { rotate: `${-20 * (1 - done.value)}deg` }] };
48
+ });
49
+ const { down } = press;
50
+ const pressed = colors.pressed;
51
+ const backgroundStyle = useAnimatedStyle(() => {
52
+ "worklet";
53
+ return { backgroundColor: down.value ? pressed : fixed.transparent };
54
+ });
55
+
56
+ return (
57
+ <Pressable accessibilityLabel={accessibilityLabel} accessibilityRole="button" hitSlop={4} onPress={copy} onPressIn={press.onPressIn} onPressOut={press.onPressOut} testID={testID}>
58
+ <Animated.View style={[styles.button, backgroundStyle, press.scaleStyle]}>
59
+ <Animated.View style={[styles.icon, copyIconStyle]}>
60
+ <Icon color={colors.text} name="copy" size={18} />
61
+ </Animated.View>
62
+ <Animated.View style={[styles.icon, checkIconStyle]}>
63
+ <Icon color={colors.success} name="check" size={18} />
64
+ </Animated.View>
65
+ </Animated.View>
66
+ </Pressable>
67
+ );
68
+ });
69
+
70
+ function clearReset(reset: { current: ReturnType<typeof setTimeout> | null }): void {
71
+ if (reset.current) clearTimeout(reset.current);
72
+ reset.current = null;
73
+ }
74
+
75
+ const styles = StyleSheet.create({
76
+ button: { width: 36, height: 36, borderRadius: 18, alignItems: "center", justifyContent: "center" },
77
+ icon: { position: "absolute" },
78
+ });
@@ -0,0 +1,90 @@
1
+ import { forwardRef, memo } from "react";
2
+ import { Pressable, StyleSheet, View, type View as ViewType } from "react-native";
3
+ import Animated, { useAnimatedStyle } from "react-native-reanimated";
4
+ import { Icon, type IconName } from "../icons/Icon";
5
+ import { usePressAnimation } from "../motion/press";
6
+ import { useTheme } from "../theme/theme";
7
+ import { fixed, type Palette } from "../theme/tokens";
8
+ import { useMeasuredRef } from "../frame/measurement";
9
+ import { ChalkText } from "./ChalkText";
10
+
11
+ export type IconButtonProps = {
12
+ readonly icon: IconName;
13
+ readonly accessibilityLabel: string;
14
+ readonly onPress?: () => void;
15
+ /** 44 pt by default, 52 pt in the phone dock, 40 pt in the header, 36 pt for a close button. */
16
+ readonly size?: 36 | 40 | 44 | 52;
17
+ /** `raised` for dock buttons, `surface` for header buttons, `danger` for Leave, `ghost` for close buttons (`.x`). */
18
+ readonly tone?: "raised" | "surface" | "danger" | "ghost";
19
+ /** An on state: the button inverts, as `aria-pressed="true"` does in the mockup. */
20
+ readonly selected?: boolean;
21
+ /** Draws the icon in the danger text colour, for a muted microphone or a camera that's off. */
22
+ readonly off?: boolean;
23
+ /** A short name under the button, such as the dock's "Speaker". */
24
+ readonly caption?: string;
25
+ /** The button opens a menu or sheet; reported as expanded while it's open. */
26
+ readonly expanded?: boolean;
27
+ /** Sits directly on the call canvas (dock, header); the light theme rings it with a hairline. */
28
+ readonly onCanvas?: boolean;
29
+ readonly disabled?: boolean;
30
+ readonly testID?: string;
31
+ };
32
+
33
+ /** The mockup's round button (`.rb`): press scale 0.94 over 150 ms, background switches at once. */
34
+ export const IconButton = memo(
35
+ forwardRef<ViewType, IconButtonProps>(function IconButton({ icon, accessibilityLabel, onPress, size = 44, tone = "raised", selected = false, off = false, caption, expanded, onCanvas = false, disabled = false, testID }, ref) {
36
+ const { colors, elevation } = useTheme();
37
+ const press = usePressAnimation("round");
38
+ const base = selected ? colors.text : BASE[tone](colors);
39
+ const pressedColor = selected ? colors.text : tone === "danger" ? colors.dangerPressed : colors.pressed;
40
+ const iconColor = selected ? colors.canvas : tone === "danger" ? fixed.onDanger : off ? colors.dangerText : tone === "ghost" ? colors.textMuted : colors.text;
41
+ const ring = onCanvas && elevation.ringOnCanvas && tone !== "danger";
42
+ const { down } = press;
43
+ const roundStyle = [styles.round, { width: size, height: size, borderRadius: size / 2 }, ring ? { boxShadow: `0px 0px 0px 1px ${colors.line}` } : null, disabled ? styles.disabled : null];
44
+ const roundRef = useMeasuredRef<ViewType>(testID, [roundStyle, { backgroundColor: base }]);
45
+ const background = useAnimatedStyle(() => {
46
+ "worklet";
47
+ return { backgroundColor: down.value ? pressedColor : base };
48
+ });
49
+
50
+ return (
51
+ <Pressable
52
+ accessibilityLabel={accessibilityLabel}
53
+ accessibilityRole="button"
54
+ accessibilityState={{ selected, disabled, expanded }}
55
+ disabled={disabled}
56
+ hitSlop={size < 44 ? (44 - size) / 2 : undefined}
57
+ onPress={onPress}
58
+ onPressIn={press.onPressIn}
59
+ onPressOut={press.onPressOut}
60
+ ref={ref}
61
+ testID={testID}
62
+ >
63
+ <Animated.View ref={roundRef} style={[roundStyle, background, press.scaleStyle]}>
64
+ <Icon color={iconColor} name={icon} size={tone === "ghost" ? 18 : 20} />
65
+ </Animated.View>
66
+ {caption ? (
67
+ <View pointerEvents="none" style={styles.captionBox}>
68
+ <ChalkText numberOfLines={1} tone="textMuted" variant="caption">
69
+ {caption}
70
+ </ChalkText>
71
+ </View>
72
+ ) : null}
73
+ </Pressable>
74
+ );
75
+ }),
76
+ );
77
+
78
+ const BASE: Readonly<Record<NonNullable<IconButtonProps["tone"]>, (colors: Palette) => string>> = {
79
+ raised: (colors) => colors.raised,
80
+ surface: (colors) => colors.surface,
81
+ danger: (colors) => colors.danger,
82
+ ghost: () => fixed.transparent,
83
+ };
84
+
85
+ const styles = StyleSheet.create({
86
+ round: { alignItems: "center", justifyContent: "center" },
87
+ disabled: { opacity: 0.45 },
88
+ // `.rl`: absolutely placed so the dock keeps its height; its bottom sits 17 pt below the button.
89
+ captionBox: { position: "absolute", left: -20, right: -20, bottom: -17, alignItems: "center" },
90
+ });
@@ -0,0 +1,212 @@
1
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode, type RefObject } from "react";
2
+ import { AccessibilityInfo, findNodeHandle, Keyboard, Pressable, ScrollView, StyleSheet, View, type LayoutChangeEvent } from "react-native";
3
+ import Animated, { useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
4
+ import { useBackHandler } from "../frame/back-handler";
5
+ import { useDeviceLayout } from "../frame/device-layout";
6
+ import { Portal } from "../frame/portal";
7
+ import { useMeasuredRef } from "../frame/measurement";
8
+ import { Icon, type IconName } from "../icons/Icon";
9
+ import { timings } from "../motion/motion";
10
+ import { useTheme } from "../theme/theme";
11
+ import { radii, sizes } from "../theme/tokens";
12
+ import { ChalkText } from "./ChalkText";
13
+ import { placeMenu, type MenuPlacement, type Rect } from "./menu-placement";
14
+
15
+ export type MenuProps = {
16
+ readonly open: boolean;
17
+ readonly onClose: () => void;
18
+ /** The button that opened the menu. The menu sits next to it and focus returns to it. */
19
+ readonly anchorRef: RefObject<View | null>;
20
+ readonly accessibilityLabel: string;
21
+ readonly children: ReactNode;
22
+ readonly minWidth?: number;
23
+ readonly testID?: string;
24
+ };
25
+
26
+ type MenuFocus = { readonly claim: (node: View) => void };
27
+ const MenuFocusContext = createContext<MenuFocus | null>(null);
28
+
29
+ /**
30
+ * The menu and popover surface (`.pop`): 12 pt radius, sentence-case section labels and
31
+ * 40 pt rows (48 pt on a phone). It opens below its trigger, flips above only when there's
32
+ * more room there, shifts sideways to stay on screen, and stays inside the safe area.
33
+ */
34
+ export function Menu({ open, onClose, anchorRef, accessibilityLabel, children, minWidth = 250, testID }: MenuProps): React.JSX.Element | null {
35
+ const { colors, elevation } = useTheme();
36
+ const layout = useDeviceLayout();
37
+ const reduceMotion = useReducedMotion();
38
+ const [trigger, setTrigger] = useState<Rect | null>(null);
39
+ const [placement, setPlacement] = useState<MenuPlacement | null>(null);
40
+ const firstItem = useRef<View | null>(null);
41
+ const keyboardHeight = useKeyboardHeight();
42
+ const shown = useSharedValue(0);
43
+
44
+ useBackHandler(open, onClose);
45
+
46
+ useEffect(() => {
47
+ if (!open) {
48
+ firstItem.current = null;
49
+ setTrigger(null);
50
+ setPlacement(null);
51
+ shown.value = 0;
52
+ return;
53
+ }
54
+ anchorRef.current?.measureInWindow((x, y, width, height) => setTrigger({ x, y, width, height }));
55
+ }, [anchorRef, open, shown]);
56
+
57
+ // Like iOS menus, an open menu closes when the screen rotates or resizes, because its trigger moved.
58
+ const viewport = `${layout.width}x${layout.height}`;
59
+ const openedIn = useRef(viewport);
60
+ useEffect(() => {
61
+ if (!open) openedIn.current = viewport;
62
+ else if (viewport !== openedIn.current) onClose();
63
+ }, [onClose, open, viewport]);
64
+
65
+ const wasOpen = useRef(open);
66
+ useEffect(() => {
67
+ const closed = wasOpen.current && !open;
68
+ wasOpen.current = open;
69
+ if (!closed || !anchorRef.current) return;
70
+ const node = findNodeHandle(anchorRef.current);
71
+ if (node) AccessibilityInfo.setAccessibilityFocus(node);
72
+ }, [anchorRef, open]);
73
+
74
+ const onLayout = useCallback(
75
+ (event: LayoutChangeEvent) => {
76
+ if (!trigger || placement) return;
77
+ const { width, height } = event.nativeEvent.layout;
78
+ setPlacement(placeMenu(trigger, { width, height }, layout, layout.insets, keyboardHeight));
79
+ shown.value = reduceMotion ? 1 : withTiming(1, timings.menu);
80
+ },
81
+ [keyboardHeight, layout, placement, reduceMotion, shown, trigger],
82
+ );
83
+
84
+ useEffect(() => {
85
+ if (!placement || !firstItem.current) return;
86
+ const node = findNodeHandle(firstItem.current);
87
+ if (node) AccessibilityInfo.setAccessibilityFocus(node);
88
+ }, [placement]);
89
+
90
+ const surfaceStyle = useAnimatedStyle(() => {
91
+ "worklet";
92
+ return { opacity: shown.value, transform: [{ translateY: reduceMotion ? 0 : (1 - shown.value) * 4 }] };
93
+ });
94
+ const focus = useMemo<MenuFocus>(
95
+ () => ({
96
+ claim: (node) => {
97
+ firstItem.current ??= node;
98
+ },
99
+ }),
100
+ [],
101
+ );
102
+
103
+ const surfaceStatic = [styles.surface, { minWidth, backgroundColor: colors.popover, borderColor: colors.lineStrong, boxShadow: elevation.popover }, placement ? { left: placement.x, top: placement.y, maxHeight: placement.maxHeight ?? undefined } : styles.measuring];
104
+ const surfaceRef = useMeasuredRef<View>(testID, surfaceStatic);
105
+
106
+ if (!open || !trigger) return null;
107
+ return (
108
+ <Portal layer="menus">
109
+ <Pressable accessible={false} onPress={onClose} style={StyleSheet.absoluteFill} testID={testID ? `${testID}.backdrop` : undefined} />
110
+ <Animated.View accessibilityLabel={accessibilityLabel} accessibilityRole="menu" accessibilityViewIsModal onAccessibilityEscape={onClose} onLayout={onLayout} ref={surfaceRef} style={[surfaceStatic, surfaceStyle]} testID={testID}>
111
+ <MenuFocusContext.Provider value={focus}>
112
+ <ScrollView bounces={false} scrollEnabled={placement?.maxHeight != null}>
113
+ {children}
114
+ </ScrollView>
115
+ </MenuFocusContext.Provider>
116
+ </Animated.View>
117
+ </Portal>
118
+ );
119
+ }
120
+
121
+ export type MenuItemProps = {
122
+ readonly label: string;
123
+ readonly onPress: () => void;
124
+ readonly icon?: IconName;
125
+ /** A smaller second line under the label (`.mi small`). */
126
+ readonly detail?: string;
127
+ /** Muted text at the end of the row (`.mi .end`). */
128
+ readonly trailing?: string;
129
+ /** Rows in a choice list reserve a check column; `true` draws the check. */
130
+ readonly checked?: boolean;
131
+ readonly danger?: boolean;
132
+ readonly testID?: string;
133
+ };
134
+
135
+ export function MenuItem({ label, onPress, icon, detail, trailing, checked, danger = false, testID }: MenuItemProps): React.JSX.Element {
136
+ const { colors } = useTheme();
137
+ const { phone } = useDeviceLayout();
138
+ const focus = useContext(MenuFocusContext);
139
+ const ref = useRef<View>(null);
140
+ useLayoutEffect(() => {
141
+ if (ref.current) focus?.claim(ref.current);
142
+ }, [focus]);
143
+ const tone = danger ? "dangerText" : "text";
144
+ return (
145
+ <Pressable
146
+ accessibilityLabel={trailing ? `${label}, ${trailing}` : label}
147
+ accessibilityRole="menuitem"
148
+ accessibilityState={checked === undefined ? undefined : { checked }}
149
+ onPress={onPress}
150
+ ref={ref}
151
+ style={({ pressed }) => [styles.item, { minHeight: phone ? sizes.menuRowPhone : sizes.menuRow }, pressed ? { backgroundColor: colors.hover } : null]}
152
+ testID={testID}
153
+ >
154
+ {checked === undefined ? null : <View style={styles.check}>{checked ? <Icon color={colors.text} name="check" size={18} /> : null}</View>}
155
+ {icon ? <Icon color={danger ? colors.dangerText : colors.textMuted} name={icon} size={18} /> : null}
156
+ <View style={styles.labels}>
157
+ <ChalkText numberOfLines={1} tone={tone}>
158
+ {label}
159
+ </ChalkText>
160
+ {detail ? (
161
+ <ChalkText tone="textMuted" variant="label">
162
+ {detail}
163
+ </ChalkText>
164
+ ) : null}
165
+ </View>
166
+ {trailing ? (
167
+ <ChalkText tone="textMuted" variant="meta">
168
+ {trailing}
169
+ </ChalkText>
170
+ ) : null}
171
+ </Pressable>
172
+ );
173
+ }
174
+
175
+ /** A sentence-case label above a group of rows (`.mh2`). */
176
+ export function MenuSection({ label }: { readonly label: string }): React.JSX.Element {
177
+ return (
178
+ <View accessibilityRole="header" accessible style={styles.section}>
179
+ <ChalkText tone="textMuted" variant="section">
180
+ {label}
181
+ </ChalkText>
182
+ </View>
183
+ );
184
+ }
185
+
186
+ export function MenuSeparator(): React.JSX.Element {
187
+ const { colors } = useTheme();
188
+ return <View style={[styles.separator, { backgroundColor: colors.line }]} />;
189
+ }
190
+
191
+ function useKeyboardHeight(): number {
192
+ const [height, setHeight] = useState(0);
193
+ useEffect(() => {
194
+ const show = Keyboard.addListener("keyboardDidShow", (event) => setHeight(event.endCoordinates.height));
195
+ const hide = Keyboard.addListener("keyboardDidHide", () => setHeight(0));
196
+ return () => {
197
+ show.remove();
198
+ hide.remove();
199
+ };
200
+ }, []);
201
+ return height;
202
+ }
203
+
204
+ const styles = StyleSheet.create({
205
+ surface: { position: "absolute", padding: 6, borderWidth: 1, borderRadius: radii.menu },
206
+ measuring: { left: 0, top: 0, opacity: 0 },
207
+ item: { flexDirection: "row", alignItems: "center", gap: 10, paddingHorizontal: 10, borderRadius: radii.menuRow },
208
+ check: { width: 18, alignItems: "center" },
209
+ labels: { flex: 1, minWidth: 0, gap: 2 },
210
+ section: { paddingTop: 8, paddingHorizontal: 10, paddingBottom: 4 },
211
+ separator: { height: 1, marginVertical: 6, marginHorizontal: 4 },
212
+ });
@@ -0,0 +1,68 @@
1
+ import { memo } from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { useMeasuredRef } from "../frame/measurement";
4
+ import { Icon, type IconName } from "../icons/Icon";
5
+ import { fixed, fontFamilies, type } from "../theme/tokens";
6
+ import { ChalkText } from "./ChalkText";
7
+
8
+ export type NameTagProps = {
9
+ readonly name: string;
10
+ readonly muted?: boolean;
11
+ readonly pinned?: boolean;
12
+ /** A leading icon for a tile that isn't a person, such as `share` on a shared screen or `pen` on the board. */
13
+ readonly icon?: IconName;
14
+ /** Your own tile: `(You)` follows the name and stays whole when a long name truncates. */
15
+ readonly you?: boolean;
16
+ /** Raised hand; `handOrder` shows the place in the queue when more than one hand is up. */
17
+ readonly handRaised?: boolean;
18
+ readonly handOrder?: number;
19
+ readonly testID?: string;
20
+ };
21
+
22
+ /** The mockup's name tag (`.tag`): bottom-left of a tile, white on the dark overlay in both themes. */
23
+ export const NameTag = memo(function NameTag({ name, muted = false, pinned = false, icon, you = false, handRaised = false, handOrder, testID }: NameTagProps): React.JSX.Element {
24
+ const tagRef = useMeasuredRef<View>(testID, styles.tag);
25
+ return (
26
+ <View pointerEvents="none" style={styles.slot}>
27
+ <View ref={tagRef} style={styles.tag} testID={testID}>
28
+ {pinned ? <Icon color={fixed.overlayText} name="pin" size={14} /> : null}
29
+ {icon ? <Icon color={fixed.overlayText} name={icon} size={14} /> : null}
30
+ <ChalkText color={fixed.overlayText} numberOfLines={1} style={styles.name} variant="label">
31
+ {name}
32
+ </ChalkText>
33
+ {you ? (
34
+ <ChalkText color={fixed.overlayText} style={styles.you} variant="label">
35
+ (You)
36
+ </ChalkText>
37
+ ) : null}
38
+ {muted ? <Icon color={fixed.overlayDanger} name="micOff" size={14} /> : null}
39
+ {handRaised ? (
40
+ <View style={styles.hand}>
41
+ <Icon color={fixed.overlayWarning} name="hand" size={14} />
42
+ {handOrder !== undefined ? (
43
+ <ChalkText color={fixed.overlayWarning} style={styles.order} tabular>
44
+ {handOrder}
45
+ </ChalkText>
46
+ ) : null}
47
+ </View>
48
+ ) : null}
49
+ </View>
50
+ </View>
51
+ );
52
+ });
53
+
54
+ const INSET = 8;
55
+ const HEIGHT = 24;
56
+
57
+ /** The strip at the bottom of a tile that the name tag can cover, so tile content can keep clear of it. */
58
+ export const nameTagZone = INSET + HEIGHT;
59
+
60
+ const styles = StyleSheet.create({
61
+ // `.tag`: 8 pt from the bottom-left corner and never wider than the tile less 16 pt.
62
+ slot: { position: "absolute", left: INSET, right: INSET, bottom: INSET, flexDirection: "row", zIndex: 2 },
63
+ tag: { flexShrink: 1, height: HEIGHT, paddingHorizontal: 8, borderRadius: 6, backgroundColor: fixed.overlay, flexDirection: "row", alignItems: "center", gap: 5 },
64
+ name: { flexShrink: 1 },
65
+ you: { flexShrink: 0 },
66
+ hand: { flexDirection: "row", alignItems: "center", gap: 2 },
67
+ order: { ...type.caption, fontFamily: fontFamilies[700] },
68
+ });
@@ -0,0 +1,56 @@
1
+ import { forwardRef, memo } from "react";
2
+ import { Pressable, StyleSheet, type View as ViewType } from "react-native";
3
+ import Animated from "react-native-reanimated";
4
+ import { Icon, type IconName } from "../icons/Icon";
5
+ import { usePressAnimation } from "../motion/press";
6
+ import { useTheme } from "../theme/theme";
7
+ import { fixed, type TypeStyle } from "../theme/tokens";
8
+ import { ChalkText } from "./ChalkText";
9
+
10
+ export type PillButtonProps = {
11
+ readonly label: string;
12
+ readonly onPress?: () => void;
13
+ readonly variant?: "primary" | "secondary" | "danger";
14
+ /**
15
+ * `sm` 32, `md` 36 (`.pb`), `lg` 40, `wide` 48 and full width (sheets), `big` 52 and full width (Home).
16
+ * Home's inline buttons: `inline` 40 (`.clipc .pb`), `card` 44 and full width (`.lcard .pb`), `field` 48 beside a field (`.yrow .pb`).
17
+ */
18
+ readonly size?: "sm" | "md" | "lg" | "wide" | "big" | "inline" | "card" | "field";
19
+ readonly icon?: IconName;
20
+ readonly accessibilityLabel?: string;
21
+ readonly disabled?: boolean;
22
+ readonly testID?: string;
23
+ };
24
+
25
+ const HEIGHT = { sm: 32, md: 36, lg: 40, wide: 48, big: 52, inline: 40, card: 44, field: 48 } as const;
26
+ const PADDING = { sm: 12, md: 14, lg: 14, wide: 14, big: 14, inline: 12, card: 14, field: 18 } as const;
27
+ const TEXT: Readonly<Record<keyof typeof HEIGHT, TypeStyle>> = { sm: "button", md: "button", lg: "bodyStrong", wide: "wideButton", big: "sheetTitle", inline: "button", card: "button", field: "button" };
28
+ const ICON = { sm: 18, md: 18, lg: 18, wide: 18, big: 20, inline: 18, card: 18, field: 18 } as const;
29
+
30
+ /** The mockup's pill button (`.pb`, `.big`): primary inverts, secondary is raised, danger is red. */
31
+ export const PillButton = memo(
32
+ forwardRef<ViewType, PillButtonProps>(function PillButton({ label, onPress, variant = "secondary", size = "md", icon, accessibilityLabel, disabled = false, testID }, ref) {
33
+ const { colors } = useTheme();
34
+ const press = usePressAnimation(size === "big" ? "big" : "pill");
35
+ // Disabled swaps the fill and label colours instead of fading the pill, so the label keeps 3:1 against its fill in both themes.
36
+ const background = disabled ? colors.hover : variant === "primary" ? colors.text : variant === "danger" ? colors.danger : size === "big" ? colors.raised : colors.hover;
37
+ const foreground = disabled ? colors.textMuted : variant === "primary" ? colors.canvas : variant === "danger" ? fixed.onDanger : colors.text;
38
+ const full = size === "wide" || size === "big" || size === "card";
39
+
40
+ return (
41
+ <Pressable accessibilityLabel={accessibilityLabel} accessibilityRole="button" accessibilityState={{ disabled }} disabled={disabled} onPress={onPress} onPressIn={press.onPressIn} onPressOut={press.onPressOut} ref={ref} style={full ? styles.full : null} testID={testID}>
42
+ <Animated.View style={[styles.pill, { height: HEIGHT[size], paddingHorizontal: PADDING[size], backgroundColor: background }, press.scaleStyle]}>
43
+ {icon ? <Icon color={foreground} name={icon} size={ICON[size]} /> : null}
44
+ <ChalkText color={foreground} numberOfLines={1} variant={TEXT[size]}>
45
+ {label}
46
+ </ChalkText>
47
+ </Animated.View>
48
+ </Pressable>
49
+ );
50
+ }),
51
+ );
52
+
53
+ const styles = StyleSheet.create({
54
+ pill: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 999 },
55
+ full: { alignSelf: "stretch" },
56
+ });
@@ -0,0 +1,55 @@
1
+ import { Pressable, StyleSheet, View } from "react-native";
2
+ import { useMeasuredRef } from "../frame/measurement";
3
+ import { useTheme } from "../theme/theme";
4
+ import { fixed } from "../theme/tokens";
5
+ import { ChalkText } from "./ChalkText";
6
+
7
+ export type SegmentOption<TValue extends string> = {
8
+ readonly value: TValue;
9
+ readonly label: string;
10
+ readonly testID?: string;
11
+ };
12
+
13
+ export type SegmentedControlProps<TValue extends string> = {
14
+ readonly options: readonly SegmentOption<TValue>[];
15
+ readonly value: TValue;
16
+ readonly onChange: (value: TValue) => void;
17
+ readonly accessibilityLabel: string;
18
+ /** `regular` is the sheet row control (38 pt segments); `compact` is the call details one (30 pt). */
19
+ readonly size?: "regular" | "compact";
20
+ readonly testID?: string;
21
+ };
22
+
23
+ /** The mockup's segmented control (`.seg2`). The selected segment lifts onto a brighter surface. */
24
+ export function SegmentedControl<TValue extends string>({ options, value, onChange, accessibilityLabel, size = "regular", testID }: SegmentedControlProps<TValue>): React.JSX.Element {
25
+ const { colors } = useTheme();
26
+ const compact = size === "compact";
27
+ const trackStyle = [styles.track, { backgroundColor: compact ? colors.hover : colors.surface }];
28
+ const trackRef = useMeasuredRef<View>(testID, trackStyle);
29
+ return (
30
+ <View accessibilityLabel={accessibilityLabel} accessibilityRole="radiogroup" ref={trackRef} style={trackStyle} testID={testID}>
31
+ {options.map((option) => (
32
+ <Segment compact={compact} key={option.value} onChange={onChange} option={option} selected={option.value === value} />
33
+ ))}
34
+ </View>
35
+ );
36
+ }
37
+
38
+ function Segment<TValue extends string>({ option, selected, compact, onChange }: { readonly option: SegmentOption<TValue>; readonly selected: boolean; readonly compact: boolean; readonly onChange: (value: TValue) => void }): React.JSX.Element {
39
+ const { colors } = useTheme();
40
+ const style = [styles.segment, { height: compact ? 30 : 38 }, selected ? (compact ? [{ backgroundColor: colors.popover }, styles.lifted] : { backgroundColor: colors.pressed }) : null];
41
+ const ref = useMeasuredRef<View>(option.testID, style);
42
+ return (
43
+ <Pressable accessibilityLabel={option.label} accessibilityRole="radio" accessibilityState={{ selected, checked: selected }} hitSlop={compact ? 7 : 3} onPress={() => onChange(option.value)} ref={ref} style={style} testID={option.testID}>
44
+ <ChalkText tone={selected ? "text" : "textMuted"} variant={compact ? "button" : "bodyStrong"}>
45
+ {option.label}
46
+ </ChalkText>
47
+ </Pressable>
48
+ );
49
+ }
50
+
51
+ const styles = StyleSheet.create({
52
+ track: { flexDirection: "row", padding: 3, borderRadius: 999 },
53
+ segment: { justifyContent: "center", paddingHorizontal: 14, borderRadius: 999 },
54
+ lifted: { boxShadow: fixed.segmentShadow },
55
+ });
@@ -0,0 +1,72 @@
1
+ import { Children, Fragment, type ReactNode } from "react";
2
+ import { Pressable, StyleSheet, View } from "react-native";
3
+ import { Icon, type IconName } from "../icons/Icon";
4
+ import { useTheme } from "../theme/theme";
5
+ import { ChalkText } from "./ChalkText";
6
+
7
+ /** A rounded group of sheet rows (`.sgrp`), with a hairline between rows. */
8
+ export function SheetGroup({ children, testID }: { readonly children: ReactNode; readonly testID?: string }): React.JSX.Element {
9
+ const { colors } = useTheme();
10
+ const rows = Children.toArray(children);
11
+ return (
12
+ <View style={[styles.group, { backgroundColor: colors.raised }]} testID={testID}>
13
+ {rows.map((row, index) => (
14
+ <Fragment key={index}>
15
+ {index > 0 ? <View style={[styles.divider, { backgroundColor: colors.line }]} /> : null}
16
+ {row}
17
+ </Fragment>
18
+ ))}
19
+ </View>
20
+ );
21
+ }
22
+
23
+ export type SheetRowProps = {
24
+ readonly label: string;
25
+ readonly icon?: IconName;
26
+ /** A control or muted text at the end of the row: a switch, a segmented control, a count. */
27
+ readonly end?: ReactNode;
28
+ /** A mark right after the label, such as the unread dot (`.udot`); the end control stays at the edge. */
29
+ readonly badge?: ReactNode;
30
+ /** What a screen reader says for the row, when the badge adds meaning the label lacks. Defaults to the label. */
31
+ readonly accessibilityLabel?: string;
32
+ /** Makes the whole row a button. Leave it out when the row holds its own control. */
33
+ readonly onPress?: () => void;
34
+ readonly danger?: boolean;
35
+ readonly testID?: string;
36
+ };
37
+
38
+ /** A phone sheet row (`.srow`): 52 pt high, 16 pt text, an optional icon in front and a control at the end. */
39
+ export function SheetRow({ label, icon, end, badge, accessibilityLabel = label, onPress, danger = false, testID }: SheetRowProps): React.JSX.Element {
40
+ const { colors } = useTheme();
41
+ const content = (
42
+ <>
43
+ {icon ? <Icon color={danger ? colors.dangerText : colors.textSoft} name={icon} /> : null}
44
+ <ChalkText numberOfLines={1} style={badge ? styles.labelShrink : styles.label} tone={danger ? "dangerText" : "text"} variant="phoneRow">
45
+ {label}
46
+ </ChalkText>
47
+ {badge ? <View style={styles.badge}>{badge}</View> : null}
48
+ {end}
49
+ </>
50
+ );
51
+ if (!onPress) {
52
+ return (
53
+ <View style={styles.row} testID={testID}>
54
+ {content}
55
+ </View>
56
+ );
57
+ }
58
+ return (
59
+ <Pressable accessibilityLabel={accessibilityLabel} accessibilityRole="button" onPress={onPress} style={({ pressed }) => [styles.row, pressed ? { backgroundColor: colors.pressed } : null]} testID={testID}>
60
+ {content}
61
+ </Pressable>
62
+ );
63
+ }
64
+
65
+ const styles = StyleSheet.create({
66
+ group: { borderRadius: 16, overflow: "hidden" },
67
+ divider: { height: 1 },
68
+ row: { flexDirection: "row", alignItems: "center", gap: 14, minHeight: 52, paddingHorizontal: 16 },
69
+ label: { flex: 1 },
70
+ labelShrink: { flexShrink: 1 },
71
+ badge: { marginLeft: 2, marginRight: "auto" },
72
+ });
@@ -0,0 +1,39 @@
1
+ import { memo, useEffect } from "react";
2
+ import { Pressable, StyleSheet } from "react-native";
3
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
4
+ import { timings } from "../motion/motion";
5
+ import { useTheme } from "../theme/theme";
6
+ import { fixed } from "../theme/tokens";
7
+
8
+ export type SwitchProps = {
9
+ readonly value: boolean;
10
+ readonly onValueChange: (value: boolean) => void;
11
+ readonly accessibilityLabel: string;
12
+ readonly testID?: string;
13
+ };
14
+
15
+ /** The mockup's switch (`.sw`): a 36 by 22 track, green when on, with a knob that slides 14 pt in 150 ms. */
16
+ export const Switch = memo(function Switch({ value, onValueChange, accessibilityLabel, testID }: SwitchProps): React.JSX.Element {
17
+ const { colors } = useTheme();
18
+ const on = useSharedValue(value ? 1 : 0);
19
+ useEffect(() => {
20
+ on.value = withTiming(value ? 1 : 0, timings.switch);
21
+ }, [on, value]);
22
+ const knobStyle = useAnimatedStyle(() => {
23
+ "worklet";
24
+ return { transform: [{ translateX: on.value * 14 }] };
25
+ });
26
+
27
+ return (
28
+ <Pressable accessibilityLabel={accessibilityLabel} accessibilityRole="switch" accessibilityState={{ checked: value }} hitSlop={11} onPress={() => onValueChange(!value)} testID={testID}>
29
+ <Animated.View style={[styles.track, { backgroundColor: value ? colors.success : colors.pressed }]}>
30
+ <Animated.View style={[styles.knob, knobStyle]} />
31
+ </Animated.View>
32
+ </Pressable>
33
+ );
34
+ });
35
+
36
+ const styles = StyleSheet.create({
37
+ track: { width: 36, height: 22, borderRadius: 999 },
38
+ knob: { position: "absolute", left: 3, top: 3, width: 16, height: 16, borderRadius: 8, backgroundColor: fixed.switchKnob },
39
+ });