@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,159 @@
1
+ import { useEffect, useRef } from "react";
2
+ import { AccessibilityInfo, findNodeHandle, StyleSheet, View } from "react-native";
3
+ import Animated, { cancelAnimation, Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withRepeat, withTiming } from "react-native-reanimated";
4
+ import { useMeasuredRef } from "../frame/measurement";
5
+ import { Icon } from "../icons/Icon";
6
+ import { ChalkText } from "../primitives/ChalkText";
7
+ import { PillButton } from "../primitives/PillButton";
8
+ import { useTheme } from "../theme/theme";
9
+ import { fontFamilies } from "../theme/tokens";
10
+
11
+ /** The waiting states that sit over the blurred call (T1, T3 to T6). */
12
+ export type LimboState =
13
+ | { readonly kind: "joining"; readonly rejoining: boolean }
14
+ | { readonly kind: "knocking"; readonly onCancel: () => void }
15
+ | { readonly kind: "refused"; readonly onAskAgain: () => void }
16
+ | { readonly kind: "reconnecting"; readonly queuedMessages: number }
17
+ | { readonly kind: "hold" };
18
+
19
+ export type LimboProps = {
20
+ readonly state: LimboState;
21
+ readonly callName: string;
22
+ /** Shows the recording consent line while joining or knocking. */
23
+ readonly recording: boolean;
24
+ };
25
+
26
+ /** The limbo card (`.lmsg`) centred over the veiled call, with the mockup's copy for each state. */
27
+ export function Limbo({ state, callName, recording }: LimboProps): React.JSX.Element {
28
+ const { colors } = useTheme();
29
+ const cardStyle = [styles.card, { backgroundColor: colors.surface, borderColor: colors.line }];
30
+ const cardRef = useMeasuredRef<View>("limbo.card", cardStyle);
31
+ const actionRef = useRef<View>(null);
32
+ const consent = recording && (state.kind === "joining" || state.kind === "knocking") ? <Consent /> : null;
33
+ // The mockup moves focus to the card's button when a state with one appears.
34
+ useEffect(() => {
35
+ const node = actionRef.current && findNodeHandle(actionRef.current);
36
+ if (node) AccessibilityInfo.setAccessibilityFocus(node);
37
+ }, [state.kind]);
38
+
39
+ return (
40
+ <View pointerEvents="box-none" style={styles.limbo} testID="frame.limbo">
41
+ <View accessibilityLiveRegion={state.kind === "refused" ? "assertive" : "polite"} accessibilityRole={state.kind === "refused" ? "alert" : "summary"} ref={cardRef} style={cardStyle} testID="limbo.card">
42
+ {state.kind === "joining" ? (
43
+ <Progress message={`${state.rejoining ? "Rejoining" : "Joining"} ${callName}…`} />
44
+ ) : state.kind === "knocking" ? (
45
+ <>
46
+ <Title>{`Asking to join ${callName}`}</Title>
47
+ <Line>The host will let you in.</Line>
48
+ {consent}
49
+ <View style={styles.action}>
50
+ <PillButton label="Cancel" onPress={state.onCancel} ref={actionRef} testID="limbo.cancel" />
51
+ </View>
52
+ </>
53
+ ) : state.kind === "refused" ? (
54
+ <>
55
+ <Title>The host didn’t let you in</Title>
56
+ <View style={styles.action}>
57
+ <PillButton label="Ask again" onPress={state.onAskAgain} ref={actionRef} testID="limbo.ask-again" variant="primary" />
58
+ </View>
59
+ </>
60
+ ) : state.kind === "reconnecting" ? (
61
+ <>
62
+ <Progress message="Reconnecting…" />
63
+ {state.queuedMessages > 0 ? <Line>{`${state.queuedMessages} message${state.queuedMessages > 1 ? "s" : ""} will send when you’re back`}</Line> : null}
64
+ </>
65
+ ) : (
66
+ <>
67
+ <HoldIcon />
68
+ <Title>Your call is on hold</Title>
69
+ <Line>{`You’re on a phone call. You’ll be back in ${callName} when it ends.`}</Line>
70
+ </>
71
+ )}
72
+ {state.kind === "joining" ? consent : null}
73
+ </View>
74
+ </View>
75
+ );
76
+ }
77
+
78
+ function Title({ children }: { readonly children: string }): React.JSX.Element {
79
+ return (
80
+ <ChalkText accessibilityRole="header" style={styles.center} testID="limbo.title" variant="limboTitle">
81
+ {children}
82
+ </ChalkText>
83
+ );
84
+ }
85
+
86
+ function Line({ children }: { readonly children: string }): React.JSX.Element {
87
+ return (
88
+ <ChalkText style={styles.center} testID="limbo.line" tone="textSoft">
89
+ {children}
90
+ </ChalkText>
91
+ );
92
+ }
93
+
94
+ function Progress({ message }: { readonly message: string }): React.JSX.Element {
95
+ return (
96
+ <View style={styles.row}>
97
+ <Spinner />
98
+ <ChalkText testID="limbo.progress" variant="progress">
99
+ {message}
100
+ </ChalkText>
101
+ </View>
102
+ );
103
+ }
104
+
105
+ /** `.hico`: the earpiece in green on a raised circle. */
106
+ function HoldIcon(): React.JSX.Element {
107
+ const { colors } = useTheme();
108
+ const style = [styles.holdIcon, { backgroundColor: colors.raised }];
109
+ const ref = useMeasuredRef<View>("limbo.hold-icon", style);
110
+ return (
111
+ <View ref={ref} style={style} testID="limbo.hold-icon">
112
+ <Icon color={colors.success} name="earpiece" />
113
+ </View>
114
+ );
115
+ }
116
+
117
+ /** `.consent`: everyone who joins a recorded call is told before they're in. */
118
+ function Consent(): React.JSX.Element {
119
+ const { colors } = useTheme();
120
+ return (
121
+ <View style={styles.consent} testID="limbo.consent">
122
+ <Icon color={colors.dangerText} name="rec" size={14} />
123
+ <ChalkText tone="textMuted" variant="label" style={styles.consentText}>
124
+ This call is recorded. By joining, you agree to be recorded.
125
+ </ChalkText>
126
+ </View>
127
+ );
128
+ }
129
+
130
+ /** `.spin`: an 18 pt ring with a bright quarter, one turn every 0.9 s; still with reduced motion. */
131
+ function Spinner(): React.JSX.Element {
132
+ const { colors } = useTheme();
133
+ const reduceMotion = useReducedMotion();
134
+ const turn = useSharedValue(0);
135
+ useEffect(() => {
136
+ if (reduceMotion) return;
137
+ turn.value = withRepeat(withTiming(1, { duration: 900, easing: Easing.linear }), -1);
138
+ return () => cancelAnimation(turn);
139
+ }, [reduceMotion, turn]);
140
+ const style = useAnimatedStyle(() => {
141
+ "worklet";
142
+ return { transform: [{ rotate: `${turn.value * 360}deg` }] };
143
+ });
144
+ return <Animated.View style={[styles.spinner, { borderColor: colors.lineStrong, borderTopColor: reduceMotion ? colors.textMuted : colors.text }, style]} />;
145
+ }
146
+
147
+ const styles = StyleSheet.create({
148
+ limbo: { ...StyleSheet.absoluteFillObject, alignItems: "center", justifyContent: "center" },
149
+ card: { alignItems: "center", gap: 8, maxWidth: "100%", paddingVertical: 22, paddingHorizontal: 28, borderWidth: 1, borderRadius: 16, marginHorizontal: 16 },
150
+ row: { flexDirection: "row", alignItems: "center", gap: 10 },
151
+ center: { textAlign: "center" },
152
+ // `.lmsg .pb` adds 10 pt above the button on top of the card's 8 pt gap.
153
+ action: { marginTop: 10 },
154
+ holdIcon: { width: 44, height: 44, borderRadius: 22, alignItems: "center", justifyContent: "center", marginBottom: 4 },
155
+ consent: { flexDirection: "row", alignItems: "center", gap: 6, marginTop: 4 },
156
+ // `.consent` is 12 pt at the body weight.
157
+ consentText: { fontFamily: fontFamilies[400], flexShrink: 1 },
158
+ spinner: { width: 18, height: 18, borderRadius: 9, borderWidth: 2 },
159
+ });
@@ -0,0 +1,36 @@
1
+ import { useEffect } from "react";
2
+ import { StyleSheet } from "react-native";
3
+ import Animated, { ReduceMotion, useAnimatedStyle, useSharedValue, withTiming } from "react-native-reanimated";
4
+ import { useTheme } from "../theme/theme";
5
+ import { durations, easings } from "../theme/tokens";
6
+
7
+ /**
8
+ * How much of the call shows through. The mockup blurs the call (18 pt while joining or
9
+ * knocking, 10 pt and greyed while reconnecting or on hold); without a native blur view the
10
+ * veil covers it with the canvas colour instead.
11
+ */
12
+ export type VeilLevel = "none" | "limbo" | "paused";
13
+
14
+ const OPACITY: Readonly<Record<VeilLevel, number>> = { none: 0, limbo: 0.85, paused: 0.75 };
15
+
16
+ /** `.veil`: the call lifts out of limbo by fading the canvas from 60 % to nothing over 300 ms. */
17
+ const LIFT_FROM = 0.6;
18
+
19
+ export function Veil({ level }: { readonly level: VeilLevel }): React.JSX.Element {
20
+ const { colors } = useTheme();
21
+ const opacity = useSharedValue(OPACITY[level]);
22
+ useEffect(() => {
23
+ if (level !== "none") {
24
+ opacity.value = OPACITY[level];
25
+ return;
26
+ }
27
+ if (opacity.value === 0) return;
28
+ opacity.value = LIFT_FROM;
29
+ opacity.value = withTiming(0, { duration: durations.veil, easing: easings.easeOut, reduceMotion: ReduceMotion.System });
30
+ }, [level, opacity]);
31
+ const style = useAnimatedStyle(() => {
32
+ "worklet";
33
+ return { opacity: opacity.value };
34
+ });
35
+ return <Animated.View pointerEvents={level === "none" ? "none" : "auto"} style={[StyleSheet.absoluteFill, { backgroundColor: colors.canvas }, style]} testID="limbo.veil" />;
36
+ }
@@ -0,0 +1,19 @@
1
+ import { describeMediaCaptureError } from "@q9labsai/chalk-client";
2
+ import { describe, expect, it } from "vitest";
3
+ import { toNativeCaptureError } from "./native-capture-error";
4
+
5
+ describe("toNativeCaptureError", () => {
6
+ it("makes a native permission denial recoverable through the shared classifier", () => {
7
+ const denied = toNativeCaptureError({ name: "SecurityError", message: "Permission denied." });
8
+
9
+ expect(describeMediaCaptureError(denied).code).toBe("permission_denied");
10
+ });
11
+
12
+ it("leaves every other capture error as it was", () => {
13
+ const insecure = { name: "SecurityError", message: "The operation is insecure." };
14
+ const notFound = { name: "NotFoundError", message: "Requested device not found" };
15
+
16
+ expect(toNativeCaptureError(insecure)).toBe(insecure);
17
+ expect(toNativeCaptureError(notFound)).toBe(notFound);
18
+ });
19
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@cloudflare/react-native-webrtc` reports a denied microphone or camera as a `SecurityError`, which the
3
+ * shared capture classifier reads as an unsupported environment. A denial is a `NotAllowedError` on the
4
+ * web, so the native adapter gives it that name and the call can offer Open Settings.
5
+ */
6
+ export function toNativeCaptureError(cause: unknown): unknown {
7
+ if (!isNativePermissionDenial(cause)) return cause;
8
+ return Object.assign(new Error(cause.message), { name: "NotAllowedError", cause });
9
+ }
10
+
11
+ function isNativePermissionDenial(cause: unknown): cause is { readonly name: "SecurityError"; readonly message: "Permission denied." } {
12
+ return typeof cause === "object" && cause !== null && "name" in cause && cause.name === "SecurityError" && "message" in cause && cause.message === "Permission denied.";
13
+ }
@@ -0,0 +1,101 @@
1
+ import { useEffect, useRef, type ReactNode } from "react";
2
+ import { StyleSheet, type StyleProp, type ViewStyle } from "react-native";
3
+ import Animated, { runOnUI, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
4
+ import { timings } from "./motion";
5
+
6
+ export type Frame = {
7
+ readonly x: number;
8
+ readonly y: number;
9
+ readonly width: number;
10
+ readonly height: number;
11
+ };
12
+
13
+ export type MovingFrameProps = {
14
+ readonly frame: Frame;
15
+ readonly children: ReactNode;
16
+ readonly style?: StyleProp<ViewStyle>;
17
+ readonly testID?: string;
18
+ };
19
+
20
+ /**
21
+ * Places its children in `frame` and, when `frame` changes, moves them there from where they
22
+ * were drawn: the view takes its new box at once and a translate-and-scale transform plays from
23
+ * the old box to identity over 220 ms on the UI thread. The children are never remounted, so a
24
+ * video view inside keeps its surface. A move that starts mid-move begins from the current
25
+ * on-screen box. Reduced motion jumps straight to the new box.
26
+ *
27
+ * Example, a stage tile keyed by participant so React keeps the same view across layouts:
28
+ *
29
+ * ```tsx
30
+ * {tiles.map((tile) => (
31
+ * <MovingFrame frame={tile.frame} key={tile.participantId} testID={`tile.${tile.participantId}`}>
32
+ * <TileShell cameraOn={tile.cameraOn} level={tile.level}>
33
+ * <ParticipantVideo participantId={tile.participantId} />
34
+ * <NameTag name={tile.name} muted={tile.muted} />
35
+ * </TileShell>
36
+ * </MovingFrame>
37
+ * ))}
38
+ * ```
39
+ */
40
+ export function MovingFrame({ frame, children, style, testID }: MovingFrameProps): React.JSX.Element {
41
+ const reduceMotion = useReducedMotion();
42
+ const target = useSharedValue<Frame>(frame);
43
+ const from = useSharedValue<Frame>(frame);
44
+ const progress = useSharedValue(1);
45
+ const mounted = useRef(false);
46
+
47
+ const { x, y, width, height } = frame;
48
+ useEffect(() => {
49
+ if (!mounted.current) {
50
+ mounted.current = true;
51
+ return;
52
+ }
53
+ runOnUI((next: Frame, instant: boolean) => {
54
+ "worklet";
55
+ const t = progress.value;
56
+ const was = target.value;
57
+ const start = from.value;
58
+ const shown: Frame = {
59
+ x: was.x + (start.x - was.x) * (1 - t),
60
+ y: was.y + (start.y - was.y) * (1 - t),
61
+ width: was.width + (start.width - was.width) * (1 - t),
62
+ height: was.height + (start.height - was.height) * (1 - t),
63
+ };
64
+ target.value = next;
65
+ if (instant) {
66
+ from.value = next;
67
+ progress.value = 1;
68
+ return;
69
+ }
70
+ from.value = shown;
71
+ progress.value = 0;
72
+ progress.value = withTiming(1, timings.move);
73
+ })({ x, y, width, height }, reduceMotion);
74
+ }, [x, y, width, height, reduceMotion, from, progress, target]);
75
+
76
+ const animatedStyle = useAnimatedStyle(() => {
77
+ "worklet";
78
+ const next = target.value;
79
+ const start = from.value;
80
+ const rest = 1 - progress.value;
81
+ const scaleX = next.width > 0 ? 1 + (start.width / next.width - 1) * rest : 1;
82
+ const scaleY = next.height > 0 ? 1 + (start.height / next.height - 1) * rest : 1;
83
+ return {
84
+ left: next.x,
85
+ top: next.y,
86
+ width: next.width,
87
+ height: next.height,
88
+ transform: [{ translateX: (start.x - next.x) * rest }, { translateY: (start.y - next.y) * rest }, { scaleX }, { scaleY }],
89
+ };
90
+ });
91
+
92
+ return (
93
+ <Animated.View style={[styles.frame, style, animatedStyle]} testID={testID}>
94
+ {children}
95
+ </Animated.View>
96
+ );
97
+ }
98
+
99
+ const styles = StyleSheet.create({
100
+ frame: { position: "absolute", transformOrigin: "left top" },
101
+ });
@@ -0,0 +1,33 @@
1
+ import { ReduceMotion, useReducedMotion, withTiming, type WithTimingConfig } from "react-native-reanimated";
2
+ import { durations, easings } from "../theme/tokens";
3
+
4
+ export { useReducedMotion };
5
+
6
+ /**
7
+ * Named timings from the mockup. Each one also honours the system's reduced-motion setting,
8
+ * which makes the change instant.
9
+ */
10
+ export const timings = {
11
+ press: { duration: durations.press, easing: easings.easeOut, reduceMotion: ReduceMotion.System },
12
+ fade: { duration: durations.chromeFade, easing: easings.easeOut, reduceMotion: ReduceMotion.System },
13
+ menu: { duration: durations.menu, easing: easings.emphasized, reduceMotion: ReduceMotion.System },
14
+ toastIn: { duration: durations.toastIn, easing: easings.emphasized, reduceMotion: ReduceMotion.System },
15
+ toastOut: { duration: durations.toastOut, easing: easings.easeOut, reduceMotion: ReduceMotion.System },
16
+ scrim: { duration: durations.scrim, easing: easings.easeOut, reduceMotion: ReduceMotion.System },
17
+ sheet: { duration: durations.sheet, easing: easings.emphasized, reduceMotion: ReduceMotion.System },
18
+ move: { duration: durations.move, easing: easings.move, reduceMotion: ReduceMotion.System },
19
+ enter: { duration: durations.enter, easing: easings.move, reduceMotion: ReduceMotion.System },
20
+ leave: { duration: durations.leave, easing: easings.easeOut, reduceMotion: ReduceMotion.System },
21
+ copyMorph: { duration: durations.copyMorph, easing: easings.emphasized, reduceMotion: ReduceMotion.System },
22
+ switch: { duration: durations.press, easing: easings.easeOut, reduceMotion: ReduceMotion.System },
23
+ avatarTurn: { duration: durations.avatarTurn, easing: easings.standard, reduceMotion: ReduceMotion.System },
24
+ level: { duration: durations.speakingLevel, easing: easings.linear, reduceMotion: ReduceMotion.System },
25
+ } as const satisfies Readonly<Record<string, WithTimingConfig>>;
26
+
27
+ export type TimingName = keyof typeof timings;
28
+
29
+ /** `withTiming` with one of the named timings. Call it from a worklet or from JS. */
30
+ export function animateTo(value: number, timing: TimingName): number {
31
+ "worklet";
32
+ return withTiming(value, timings[timing]);
33
+ }
@@ -0,0 +1,20 @@
1
+ import { withTiming, type EntryExitAnimationFunction } from "react-native-reanimated";
2
+ import { timings } from "./motion";
3
+
4
+ /** A new tile fades in from 97 % scale over 220 ms. Pass as `entering` on an Animated.View. */
5
+ export const tileEntering: EntryExitAnimationFunction = () => {
6
+ "worklet";
7
+ return {
8
+ initialValues: { opacity: 0, transform: [{ scale: 0.97 }] },
9
+ animations: { opacity: withTiming(1, timings.enter), transform: [{ scale: withTiming(1, timings.enter) }] },
10
+ };
11
+ };
12
+
13
+ /** A leaving tile fades out over 160 ms. Pass as `exiting` on an Animated.View. */
14
+ export const tileLeaving: EntryExitAnimationFunction = () => {
15
+ "worklet";
16
+ return {
17
+ initialValues: { opacity: 1 },
18
+ animations: { opacity: withTiming(0, timings.leave) },
19
+ };
20
+ };
@@ -0,0 +1,35 @@
1
+ import { useCallback } from "react";
2
+ import { useAnimatedStyle, useSharedValue, withTiming, type SharedValue } from "react-native-reanimated";
3
+ import { pressScale } from "../theme/tokens";
4
+ import { timings } from "./motion";
5
+
6
+ export type PressAnimation = {
7
+ /** 1 while the finger is down, 0 otherwise; flips at once, for instant colour changes. */
8
+ readonly down: SharedValue<number>;
9
+ readonly onPressIn: () => void;
10
+ readonly onPressOut: () => void;
11
+ readonly scaleStyle: ReturnType<typeof useAnimatedStyle>;
12
+ };
13
+
14
+ /**
15
+ * The mockup's `:active` press: the control scales down over 150 ms ease-out while its
16
+ * background switches at once. Runs on the UI thread; reduced motion keeps the scale at 1.
17
+ */
18
+ export function usePressAnimation(family: keyof typeof pressScale): PressAnimation {
19
+ const down = useSharedValue(0);
20
+ const scale = useSharedValue(1);
21
+ const pressedScale = pressScale[family];
22
+ const onPressIn = useCallback(() => {
23
+ down.value = 1;
24
+ scale.value = withTiming(pressedScale, timings.press);
25
+ }, [down, pressedScale, scale]);
26
+ const onPressOut = useCallback(() => {
27
+ down.value = 0;
28
+ scale.value = withTiming(1, timings.press);
29
+ }, [down, scale]);
30
+ const scaleStyle = useAnimatedStyle(() => {
31
+ "worklet";
32
+ return { transform: [{ scale: scale.value }] };
33
+ });
34
+ return { down, onPressIn, onPressOut, scaleStyle };
35
+ }
@@ -0,0 +1,211 @@
1
+ import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode, type RefObject } from "react";
2
+ import { AccessibilityInfo, findNodeHandle, Pressable, StyleSheet, View, type LayoutChangeEvent } from "react-native";
3
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
4
+ import Animated, { runOnJS, useAnimatedKeyboard, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming, type SharedValue } from "react-native-reanimated";
5
+ import { useBackHandler } from "../frame/back-handler";
6
+ import { useDeviceLayout } from "../frame/device-layout";
7
+ import { Portal } from "../frame/portal";
8
+ import { useMeasuredRef } from "../frame/measurement";
9
+ import { haptics } from "../haptics/haptics";
10
+ import { timings } from "../motion/motion";
11
+ import { useTheme } from "../theme/theme";
12
+ import { durations, fixed, radii } from "../theme/tokens";
13
+ import { ChalkText } from "./ChalkText";
14
+
15
+ export type BottomSheetProps = {
16
+ readonly open: boolean;
17
+ readonly onClose: () => void;
18
+ readonly accessibilityLabel: string;
19
+ readonly children: ReactNode;
20
+ /** A header line under the grabber (`.shd`); it takes accessibility focus when the sheet opens. */
21
+ readonly title?: string;
22
+ /** A fixed height as a share of the screen; People and Chat use 0.78. Without it the sheet fits its content. */
23
+ readonly heightRatio?: number;
24
+ /** Side padding: 12 for menu-like sheets (`.sheet`), 20 for card-like ones (`.ready`). */
25
+ readonly inset?: 12 | 14 | 20;
26
+ /**
27
+ * `card` is the People and Chat card (`.phone .card`): no side padding, the grabber 8 pt from the
28
+ * top with nothing under it, and no bottom padding, because its content runs to the bottom edge
29
+ * and pads itself for the safe area. `popover` is call details (`.phone .infop`): the popover surface
30
+ * with a top border.
31
+ */
32
+ readonly variant?: "sheet" | "card" | "popover";
33
+ /** Where accessibility focus returns when the sheet closes, usually the button that opened it. */
34
+ readonly returnFocusRef?: RefObject<View | null>;
35
+ readonly testID?: string;
36
+ };
37
+
38
+ /** Letting go after dragging the sheet down further than this closes it. */
39
+ const CLOSE_DISTANCE = 80;
40
+ /** Padding under the content: on top of the safe area when the keyboard is down, alone when it's up. */
41
+ const BOTTOM_PADDING = 10;
42
+ const KEYBOARD_PADDING = 12;
43
+
44
+ type SheetDrag = { readonly drag: SharedValue<number>; readonly close: () => void };
45
+ const SheetDragContext = createContext<SheetDrag | null>(null);
46
+
47
+ /**
48
+ * The phone bottom sheet. It runs to the very bottom edge of the screen with the safe area as
49
+ * padding inside, and only its top corners are rounded. Drag the grabber (or a `SheetDragRegion`)
50
+ * down to close; the sheet follows the finger on the UI thread. It rides above the keyboard,
51
+ * Android back closes the top sheet, and accessibility focus moves in and back out.
52
+ */
53
+ export function BottomSheet({ open, onClose, accessibilityLabel, children, title, heightRatio, inset = 12, variant = "sheet", returnFocusRef, testID }: BottomSheetProps): React.JSX.Element | null {
54
+ const { colors } = useTheme();
55
+ const layout = useDeviceLayout();
56
+ const reduceMotion = useReducedMotion();
57
+ // Expo 55 apps are edge-to-edge on Android, so the keyboard height already counts from the screen edge.
58
+ const keyboard = useAnimatedKeyboard();
59
+ const [mounted, setMounted] = useState(open);
60
+ const height = useSharedValue(0);
61
+ const offset = useSharedValue(layout.height);
62
+ const drag = useSharedValue(0);
63
+ const shown = useSharedValue(0);
64
+ const titleRef = useRef<View>(null);
65
+
66
+ useBackHandler(open, onClose);
67
+
68
+ const show = useCallback(() => {
69
+ drag.value = 0;
70
+ if (reduceMotion) {
71
+ offset.value = 0;
72
+ shown.value = 1;
73
+ } else {
74
+ offset.value = withTiming(0, timings.sheet);
75
+ shown.value = withTiming(1, timings.scrim);
76
+ }
77
+ const node = titleRef.current && findNodeHandle(titleRef.current);
78
+ if (node) setTimeout(() => AccessibilityInfo.setAccessibilityFocus(node), durations.sheet);
79
+ }, [drag, offset, reduceMotion, shown]);
80
+
81
+ const hide = useCallback(() => {
82
+ if (reduceMotion) {
83
+ shown.value = 0;
84
+ offset.value = height.value || layout.height;
85
+ setMounted(false);
86
+ return;
87
+ }
88
+ const finish = (finished?: boolean) => {
89
+ "worklet";
90
+ if (finished) runOnJS(setMounted)(false);
91
+ };
92
+ shown.value = withTiming(0, timings.scrim);
93
+ offset.value = withTiming(height.value || layout.height, timings.sheet, finish);
94
+ }, [height, layout.height, offset, reduceMotion, shown]);
95
+
96
+ const wasOpen = useRef(open);
97
+ useEffect(() => {
98
+ if (open === wasOpen.current) return;
99
+ wasOpen.current = open;
100
+ if (!open) return hide();
101
+ setMounted(true);
102
+ if (height.value > 0) show();
103
+ }, [height, hide, open, show]);
104
+
105
+ const wasMounted = useRef(mounted);
106
+ useEffect(() => {
107
+ const closed = wasMounted.current && !mounted;
108
+ wasMounted.current = mounted;
109
+ if (!closed || !returnFocusRef?.current) return;
110
+ const node = findNodeHandle(returnFocusRef.current);
111
+ if (node) AccessibilityInfo.setAccessibilityFocus(node);
112
+ }, [mounted, returnFocusRef]);
113
+
114
+ const onLayout = useCallback(
115
+ (event: LayoutChangeEvent) => {
116
+ const first = height.value === 0;
117
+ const measured = event.nativeEvent.layout.height;
118
+ height.value = measured;
119
+ if (!first || !open) return;
120
+ // A JS-thread write reaches the UI thread later, so reading height.value back here would still give 0.
121
+ offset.value = reduceMotion ? 0 : measured;
122
+ show();
123
+ },
124
+ [height, offset, open, reduceMotion, show],
125
+ );
126
+
127
+ const card = variant === "card";
128
+ const sheetStyle = useAnimatedStyle(() => {
129
+ "worklet";
130
+ return {
131
+ opacity: reduceMotion ? shown.value : 1,
132
+ paddingBottom: card ? 0 : keyboard.height.value > 0 ? KEYBOARD_PADDING : layout.insets.bottom + BOTTOM_PADDING,
133
+ maxHeight: layout.height - layout.insets.top - 8 - keyboard.height.value,
134
+ transform: [{ translateY: offset.value + drag.value - keyboard.height.value }],
135
+ };
136
+ });
137
+ const scrimStyle = useAnimatedStyle(() => {
138
+ "worklet";
139
+ return { opacity: shown.value };
140
+ });
141
+
142
+ const width = layout.arrangement === "pad" ? 440 : layout.arrangement === "landscape" ? 460 : layout.width;
143
+ const surfaceStyle = [
144
+ styles.sheet,
145
+ { backgroundColor: variant === "popover" ? colors.popover : colors.surface, width, left: (layout.width - width) / 2, paddingHorizontal: card ? 0 : inset },
146
+ card ? { borderTopWidth: 1, borderColor: colors.line } : null,
147
+ variant === "popover" ? { borderTopWidth: 1, borderColor: colors.lineStrong } : null,
148
+ heightRatio ? { height: layout.height * heightRatio } : null,
149
+ ];
150
+ const surfaceRef = useMeasuredRef<View>(testID, surfaceStyle);
151
+
152
+ if (!mounted) return null;
153
+ return (
154
+ <Portal layer="sheets">
155
+ <Animated.View style={[StyleSheet.absoluteFill, { backgroundColor: fixed.scrim }, scrimStyle]}>
156
+ <Pressable accessible={false} onPress={onClose} style={StyleSheet.absoluteFill} testID={testID ? `${testID}.scrim` : undefined} />
157
+ </Animated.View>
158
+ <SheetDragContext.Provider value={{ drag, close: onClose }}>
159
+ <Animated.View accessibilityLabel={accessibilityLabel} accessibilityViewIsModal onAccessibilityEscape={onClose} onLayout={onLayout} ref={surfaceRef} style={[surfaceStyle, sheetStyle]} testID={testID}>
160
+ <SheetDragRegion>
161
+ {/* Without a title, the grabber is where screen-reader focus lands, announced by the sheet's label. */}
162
+ <View accessibilityLabel={title ? undefined : accessibilityLabel} accessibilityRole={title ? undefined : "header"} accessible={!title} ref={title ? undefined : titleRef} style={card ? styles.cardGrabArea : styles.grabArea} testID={testID ? `${testID}.grabber` : undefined}>
163
+ <View style={[styles.grabber, { backgroundColor: colors.lineStrong }]} />
164
+ </View>
165
+ {title ? (
166
+ <View accessibilityRole="header" accessible ref={titleRef} style={styles.header}>
167
+ <ChalkText numberOfLines={1} variant="sheetTitle">
168
+ {title}
169
+ </ChalkText>
170
+ </View>
171
+ ) : null}
172
+ </SheetDragRegion>
173
+ {children}
174
+ </Animated.View>
175
+ </SheetDragContext.Provider>
176
+ </Portal>
177
+ );
178
+ }
179
+
180
+ /** Makes its children a handle that drags the enclosing sheet, like the tab row of People and Chat. */
181
+ export function SheetDragRegion({ children }: { readonly children: ReactNode }): React.JSX.Element {
182
+ const sheet = useContext(SheetDragContext);
183
+ if (!sheet) throw new Error("SheetDragRegion must be inside a BottomSheet.");
184
+ const { drag, close } = sheet;
185
+ const gesture = Gesture.Pan()
186
+ .activeOffsetY(4)
187
+ .onUpdate((event) => {
188
+ "worklet";
189
+ const next = Math.max(0, event.translationY);
190
+ if (next > CLOSE_DISTANCE !== drag.value > CLOSE_DISTANCE) runOnJS(haptics.sheetDetent)();
191
+ drag.value = next;
192
+ })
193
+ .onEnd(() => {
194
+ "worklet";
195
+ if (drag.value > CLOSE_DISTANCE) runOnJS(close)();
196
+ else drag.value = withTiming(0, timings.sheet);
197
+ });
198
+ return (
199
+ <GestureDetector gesture={gesture}>
200
+ <View collapsable={false}>{children}</View>
201
+ </GestureDetector>
202
+ );
203
+ }
204
+
205
+ const styles = StyleSheet.create({
206
+ sheet: { position: "absolute", bottom: 0, paddingTop: 8, borderTopLeftRadius: radii.sheet, borderTopRightRadius: radii.sheet },
207
+ grabArea: { alignItems: "center", paddingTop: 4, paddingBottom: 12 },
208
+ cardGrabArea: { alignItems: "center" },
209
+ grabber: { width: 36, height: 5, borderRadius: 3 },
210
+ header: { flexDirection: "row", alignItems: "center", gap: 12, paddingTop: 4, paddingHorizontal: 8, paddingBottom: 14 },
211
+ });
@@ -0,0 +1,23 @@
1
+ import { Text, type TextProps } from "react-native";
2
+ import { useMeasuredRef } from "../frame/measurement";
3
+ import { useTheme } from "../theme/theme";
4
+ import { type, type Palette, type TypeStyle } from "../theme/tokens";
5
+
6
+ export type ChalkTextProps = TextProps & {
7
+ readonly variant?: TypeStyle;
8
+ readonly tone?: keyof Palette;
9
+ /** A fixed colour for text on video or on a fixed overlay; wins over `tone`. */
10
+ readonly color?: string;
11
+ /** Tabular figures, so timers and counts don't jitter. */
12
+ readonly tabular?: boolean;
13
+ };
14
+
15
+ /** Figtree text in one of the call's type styles. */
16
+ export function ChalkText({ variant = "body", tone = "text", color, tabular = false, style, ...rest }: ChalkTextProps): React.JSX.Element {
17
+ const { colors } = useTheme();
18
+ const textStyle = [type[variant], { color: color ?? colors[tone] }, tabular ? TABULAR : null, style];
19
+ const ref = useMeasuredRef<Text>(rest.testID, textStyle);
20
+ return <Text {...rest} ref={ref} style={textStyle} />;
21
+ }
22
+
23
+ const TABULAR = { fontVariant: ["tabular-nums" as const] };