@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
@@ -1,416 +0,0 @@
1
- import type { ChatSendInput, Reaction } from "@q9labsai/chalk-client";
2
- import { useCallback, useEffect, useMemo, useState } from "react";
3
- import { Alert, Linking, Platform } from "react-native";
4
- import { mediaDevices } from "@cloudflare/react-native-webrtc";
5
-
6
- import { useSpaceClient } from "../../context/space-client-context";
7
- import { useChat as useSpaceChat, useMedia as useSpaceMedia, useParticipants as useSpaceParticipants, useReactions as useSpaceReactions, useSelf, useWhiteboard, useRecording, useConnection } from "../../hooks/space-hooks";
8
- import { getNativeJourneyContext } from "../../telemetry";
9
- import type { NativeChatMessage, NativeParticipant, NativeReaction } from "../../ui/native-types";
10
- import { isIosSimulator } from "../../utils/ios-simulator";
11
- import type { SpaceViewProps } from "../SpaceView";
12
- import { buildSpaceViewDiagnosticsSnapshot } from "./diagnostics";
13
- import { useSpaceViewDerived } from "./useSpaceViewDerived";
14
- import { useSpaceViewPanels } from "./useSpaceViewPanels";
15
- import { resolveNativeScreenShareAvailability } from "./screen-share-availability";
16
- import { normalizeChatFileDrafts, uploadAndSendNativeChatAttachments, type NativeChatFileDraft } from "./space-chat-attachments";
17
-
18
- const spaceReactions = new Set<Reaction>(["👍", "❤️", "😂", "😮", "😢", "🎉"]);
19
-
20
- /**
21
- * The native Space UI consumes the canonical SpaceClient snapshot directly.
22
- * This keeps the mature native renderer intact without re-projecting client
23
- * state through a retired compatibility layer.
24
- */
25
- export function useSpaceViewController({ spaceName, inviteLink, layout: controlledLayout, onLayoutChange, features, onLeave, onEndEpisode, onDiagnosticsChange, pickChatFiles: pickChatFilesInput }: SpaceViewProps) {
26
- const client = useSpaceClient();
27
- const self = useSelf();
28
- const participantSlice = useSpaceParticipants();
29
- const media = useSpaceMedia();
30
- const chatSlice = useSpaceChat();
31
- const reactionSlice = useSpaceReactions();
32
- const whiteboardSlice = useWhiteboard();
33
- const recording = useRecording().current;
34
- const connection = useConnection();
35
-
36
- const journeyContext = getNativeJourneyContext(client);
37
- const [actionsOpen, setActionsOpen] = useState(false);
38
- const [reactionPickerOpen, setReactionPickerOpen] = useState(false);
39
- const [chatDraft, setChatDraft] = useState("");
40
- const [chatAttachments, setChatAttachments] = useState<readonly NativeChatFileDraft[]>([]);
41
- const [chatAttachmentError, setChatAttachmentError] = useState<string | null>(null);
42
- const [chatAttachmentPicking, setChatAttachmentPicking] = useState(false);
43
- const [chatAttachmentUploading, setChatAttachmentUploading] = useState(false);
44
-
45
- const run = useCallback(async (action: () => unknown | Promise<unknown>) => {
46
- try {
47
- await action();
48
- } catch (cause) {
49
- Alert.alert("Action failed", cause instanceof Error ? cause.message : "Chalk could not complete the action.");
50
- }
51
- }, []);
52
-
53
- const localParticipantId = self.participantId;
54
- const participants = useMemo(
55
- () =>
56
- participantSlice.roster.map((participant): NativeParticipant => {
57
- const local = participant.participantId === localParticipantId;
58
- const remoteMedia = media.remote.filter((publication) => publication.participantId === participant.participantId);
59
- return {
60
- ...participant,
61
- id: participant.participantId,
62
- audioEnabled: local ? media.local.microphone.state === "enabled" : participant.media.microphone === "active",
63
- videoEnabled: local ? media.local.camera.state === "enabled" || media.local.camera.state === "requesting" : participant.media.camera === "active",
64
- audioTrack: local ? media.local.microphone.track : (remoteMedia.find((publication) => publication.source === "microphone")?.track ?? null),
65
- videoTrack: local ? media.local.camera.track : (remoteMedia.find((publication) => publication.source === "camera")?.track ?? null),
66
- screenShareTrack: local ? media.screenShare.track : (remoteMedia.find((publication) => publication.source === "screen")?.track ?? null),
67
- };
68
- }),
69
- [localParticipantId, media.local.camera, media.local.microphone, media.remote, media.screenShare.track, participantSlice.roster],
70
- );
71
- const localParticipant = useMemo(() => participants.find((participant) => participant.id === localParticipantId) ?? null, [localParticipantId, participants]);
72
- const spaceParticipants = useMemo(
73
- () => ({
74
- participants,
75
- localParticipant,
76
- remoteParticipants: participants.filter((participant) => participant.id !== localParticipantId),
77
- activeSpeaker: null,
78
- count: participants.length,
79
- participantCount: participants.length,
80
- getParticipant: (participantId: string) => participants.find((participant) => participant.id === participantId),
81
- updateDisplayName: (displayName: string) => client.participants.renameSelf(displayName),
82
- }),
83
- [client.participants, localParticipant, localParticipantId, participants],
84
- );
85
-
86
- const isLocalSharing = media.screenShare.state === "enabled" || media.screenShare.state === "requesting";
87
- const remoteScreenShare = useMemo(() => media.remote.find((publication) => publication.source === "screen") ?? null, [media.remote]);
88
- const screenShare = useMemo(
89
- () => ({
90
- isActive: isLocalSharing || remoteScreenShare !== null,
91
- isStarting: media.screenShare.state === "requesting",
92
- isLocalSharing,
93
- sharerParticipantId: isLocalSharing ? localParticipantId : (remoteScreenShare?.participantId ?? null),
94
- videoTrack: isLocalSharing ? media.screenShare.track : (remoteScreenShare?.track ?? null),
95
- audioTrack: null,
96
- start: async () => {
97
- await client.media.setScreenShareEnabled(true);
98
- return true;
99
- },
100
- stop: () => client.media.setScreenShareEnabled(false),
101
- toggle: async () => {
102
- const enabled = !isLocalSharing;
103
- await client.media.setScreenShareEnabled(enabled);
104
- return enabled;
105
- },
106
- }),
107
- [client.media, isLocalSharing, localParticipantId, media.screenShare.state, media.screenShare.track, remoteScreenShare],
108
- );
109
-
110
- const interactionState = useMemo(() => {
111
- const raisedHands = participantSlice.roster.filter((participant) => participant.handRaised).map((participant) => participant.participantId);
112
- const activeReactions: NativeReaction[] = reactionSlice.active.map((reaction) => ({
113
- id: reaction.eventId,
114
- emoji: reaction.reaction,
115
- participantId: reaction.participantId,
116
- participantName: reaction.displayName,
117
- }));
118
- return {
119
- isHandRaised: self.handRaised,
120
- raisedHands,
121
- raisedHandCount: raisedHands.length,
122
- activeReactions,
123
- reactionEnabled: self.can("sendReaction"),
124
- raiseHand: () => client.participants.raiseHand(),
125
- lowerHand: () => client.participants.lowerHand(),
126
- toggleHand: () => (self.handRaised ? client.participants.lowerHand() : client.participants.raiseHand()),
127
- sendReaction: (reaction: Reaction) => client.reactions.send(reaction),
128
- };
129
- }, [client.participants, client.reactions, participantSlice.roster, reactionSlice.active, self]);
130
-
131
- const chat = useMemo(() => {
132
- const messages: NativeChatMessage[] = chatSlice.messages.map((message) => ({
133
- id: message.messageId,
134
- sequence: message.sequence,
135
- senderId: message.participantId,
136
- senderName: message.displayName,
137
- text: message.text,
138
- content: message.text,
139
- attachments: message.attachments,
140
- readBy: chatSlice.readReceipts
141
- .filter((receipt) => receipt.participantId !== message.participantId && receipt.participantId !== localParticipantId && sequenceAtOrAfter(receipt.readThroughSequence, message.sequence))
142
- .map((receipt) => ({
143
- ...receipt,
144
- displayName: participantSlice.roster.find((participant) => participant.participantId === receipt.participantId)?.displayName ?? "Participant",
145
- })),
146
- timestamp: Date.parse(message.createdAt),
147
- }));
148
- const pendingMessages = chatSlice.pendingSends.map((pending) => ({ ...pending, state: pending.status }));
149
- const sendMessage = (content: string | ChatSendInput) => client.chat.send(typeof content === "string" ? { text: content } : content).then(() => undefined);
150
- const markAsRead = (throughSequence?: string) => {
151
- const target = throughSequence ? chatSlice.messages.find((message) => message.sequence === throughSequence) : chatSlice.messages.at(-1);
152
- return target ? client.chat.markRead(target.messageId) : Promise.resolve(null);
153
- };
154
- return {
155
- messages,
156
- isEnabled: self.can("sendChat"),
157
- count: messages.length,
158
- unreadCount: chatSlice.unreadCount,
159
- pendingMessages,
160
- hasMore: chatSlice.pagination.hasOlder,
161
- isLoadingOlder: chatSlice.status === "loading",
162
- sendMessage,
163
- retryMessage: (clientMessageId: string) => {
164
- const pending = chatSlice.pendingSends.find((message) => message.clientMessageId === clientMessageId);
165
- return pending ? client.chat.send({ text: pending.text, attachments: pending.attachments }).then(() => undefined) : Promise.reject(new Error("The pending message is no longer available."));
166
- },
167
- loadOlderMessages: () => client.chat.loadOlder().then(() => undefined),
168
- reactToMessage: () => {
169
- throw new Error("Per-message reactions are not part of the actions contract.");
170
- },
171
- markAsRead,
172
- markAsHidden: markAsRead,
173
- getMessage: (id: string) => messages.find((message) => message.id === id),
174
- };
175
- }, [chatSlice, client.chat, localParticipantId, participantSlice.roster, self]);
176
-
177
- const canWhiteboard = features?.whiteboard !== false && whiteboardSlice.engine.status !== "unsubscribed";
178
- const canDraw = canWhiteboard && self.can("drawWhiteboard");
179
- const canClear = canWhiteboard && self.can("manageWhiteboard");
180
- const whiteboardTransport = useMemo(() => client.whiteboard.transport(), [client.whiteboard, whiteboardSlice.engine.status]);
181
- const canEndEpisode = self.can("endEpisode");
182
- const screenShareAvailability = useMemo(() => resolveNativeScreenShareAvailability({ featureEnabled: features?.screenShare !== false, platform: Platform.OS, simulator: isIosSimulator(), captureAvailable: typeof mediaDevices.getDisplayMedia === "function" }), [features?.screenShare]);
183
- const capabilities = useMemo(
184
- () => ({
185
- canEndEpisode,
186
- canChat: features?.chat !== false && self.can("sendChat"),
187
- canParticipants: features?.participants !== false,
188
- canScreenShare: screenShareAvailability.enabled && self.can("publishScreen"),
189
- canReactions: features?.reactions !== false && self.can("sendReaction"),
190
- canHandRaise: features?.handRaise !== false && self.can("raiseHand"),
191
- canInvite: features?.info !== false && Boolean(inviteLink),
192
- canSettings: features?.settings !== false,
193
- canWhiteboard,
194
- canManageAdmission: features?.admission !== false && self.can("manageAdmission"),
195
- canRequestMedia: self.can("requestMediaOthers"),
196
- canMuteParticipants: self.can("muteOthers"),
197
- canRemoveParticipants: self.can("removeParticipant"),
198
- canStopParticipantCamera: self.can("stopVideoOthers"),
199
- canStopParticipantScreenShare: self.can("stopScreenOthers"),
200
- canModerate: self.can("manageAdmission") || self.can("assignRoles") || self.can("requestMediaOthers") || self.can("muteOthers") || self.can("removeParticipant") || self.can("stopVideoOthers") || self.can("stopScreenOthers"),
201
- screenShareAvailability,
202
- }),
203
- [canEndEpisode, canWhiteboard, features?.admission, features?.chat, features?.handRaise, features?.info, features?.participants, features?.reactions, features?.settings, inviteLink, screenShareAvailability, self],
204
- );
205
-
206
- const spacePanels = useSpaceViewPanels({
207
- spaceName,
208
- inviteLink,
209
- layout: controlledLayout,
210
- onLayoutChange,
211
- canWhiteboard,
212
- canDraw,
213
- canClear,
214
- canEndEpisode,
215
- transport: whiteboardTransport,
216
- onLeave,
217
- onEndEpisode,
218
- run,
219
- journeyContext,
220
- });
221
- const derived = useSpaceViewDerived({ participants, localParticipant, screenShare, isWhiteboardOpen: spacePanels.whiteboard.isOpen });
222
-
223
- useEffect(() => {
224
- const request = media.incomingRequests[0];
225
- if (!request) return;
226
- Alert.alert(request.kind === "unmute" ? "Unmute request" : "Camera request", `${request.actorDisplayName ?? "A Space organizer"} is asking you to ${request.kind === "unmute" ? "unmute" : "start your camera"}.`, [
227
- { text: "Not now", style: "cancel", onPress: () => void run(() => client.media.declineRequest(request.requestId)) },
228
- { text: "Allow", onPress: () => void run(() => client.media.acceptRequest(request.requestId)) },
229
- ]);
230
- }, [client.media, media.incomingRequests, run]);
231
-
232
- const spaceDiagnostics = useMemo(
233
- () =>
234
- buildSpaceViewDiagnosticsSnapshot({
235
- featureFlags: {
236
- chat: capabilities.canChat,
237
- participants: capabilities.canParticipants,
238
- screenShare: capabilities.canScreenShare,
239
- reactions: capabilities.canReactions,
240
- handRaise: capabilities.canHandRaise,
241
- whiteboard: capabilities.canWhiteboard,
242
- },
243
- canEndEpisode: capabilities.canEndEpisode,
244
- participantCount: spaceParticipants.participantCount,
245
- raisedHandCount: interactionState.raisedHandCount,
246
- unreadChatCount: chat.unreadCount,
247
- isScreenShareActive: screenShare.isActive,
248
- isLocalScreenSharing: screenShare.isLocalSharing,
249
- screenShareSharerParticipantId: screenShare.sharerParticipantId,
250
- canModerate: capabilities.canModerate,
251
- screenShareAvailability,
252
- }),
253
- [capabilities, chat.unreadCount, interactionState.raisedHandCount, spaceParticipants.participantCount, screenShare.isActive, screenShare.isLocalSharing, screenShare.sharerParticipantId, screenShareAvailability],
254
- );
255
- useEffect(() => onDiagnosticsChange?.(spaceDiagnostics), [onDiagnosticsChange, spaceDiagnostics]);
256
-
257
- const pickChatFiles = useCallback(async () => {
258
- if (!pickChatFilesInput) return;
259
- setChatAttachmentPicking(true);
260
- try {
261
- const picked = normalizeChatFileDrafts(await pickChatFilesInput());
262
- if (picked.error) {
263
- setChatAttachmentError(picked.error);
264
- return;
265
- }
266
- const combined = normalizeChatFileDrafts([...chatAttachments.map((draft) => draft.file), ...picked.files.map((draft) => draft.file)]);
267
- if (combined.error) {
268
- setChatAttachmentError(combined.error);
269
- return;
270
- }
271
- setChatAttachmentError(null);
272
- setChatAttachments(combined.files);
273
- } catch (cause) {
274
- setChatAttachmentError(cause instanceof Error ? cause.message : "Could not read the selected files.");
275
- } finally {
276
- setChatAttachmentPicking(false);
277
- }
278
- }, [chatAttachments, pickChatFilesInput]);
279
-
280
- const removeChatAttachment = useCallback((index: number) => {
281
- setChatAttachments((current) => current.filter((_, currentIndex) => currentIndex !== index));
282
- setChatAttachmentError(null);
283
- }, []);
284
-
285
- const sendChatMessage = useCallback(() => {
286
- const text = chatDraft.trim();
287
- if (!text && chatAttachments.length === 0) return;
288
- void run(async () => {
289
- setChatAttachmentUploading(true);
290
- setChatAttachmentError(null);
291
- try {
292
- await uploadAndSendNativeChatAttachments(chatAttachments, text, client.chat.files.upload, chat.sendMessage);
293
- setChatDraft("");
294
- setChatAttachments([]);
295
- } catch (cause) {
296
- setChatAttachmentError(cause instanceof Error ? cause.message : "Could not send the attachments.");
297
- throw cause;
298
- } finally {
299
- setChatAttachmentUploading(false);
300
- }
301
- });
302
- }, [chat, chatAttachments, chatDraft, client.chat.files, run]);
303
-
304
- const resolveChatAttachmentUrl = useCallback(
305
- async (attachmentId: string) => {
306
- const attachment = chatSlice.messages.flatMap((message) => message.attachments).find((candidate) => candidate.attachmentId === attachmentId);
307
- return attachment ? client.chat.files.resolveUrl(attachment) : null;
308
- },
309
- [chatSlice.messages, client.chat.files],
310
- );
311
-
312
- return {
313
- recording,
314
- canManageRecording: self.can("manageRecording"),
315
- canToggleRecording: connection.status === "live" && recording?.status !== "starting" && recording?.status !== "stopping",
316
- toggleRecording: () => void run(() => (recording?.status === "recording" ? client.recording.stop() : client.recording.start())),
317
- simulatorMediaDisabled: isIosSimulator(),
318
- spaceName: spaceName || "Space",
319
- panel: spacePanels.panel,
320
- selfName: self.displayName || "Participant",
321
- isMuted: media.local.microphone.state !== "enabled",
322
- isCameraOff: !(media.local.camera.state === "enabled" || media.local.camera.state === "requesting"),
323
- handRaised: interactionState.isHandRaised,
324
- raisedHandCount: interactionState.raisedHandCount,
325
- activeReactions: interactionState.activeReactions.slice(-3),
326
- secondsElapsed: spacePanels.secondsElapsed,
327
- formattedDuration: spacePanels.formattedDuration,
328
- actionsOpen,
329
- reactionPickerOpen,
330
- chatDraft,
331
- chatAttachments,
332
- chatAttachmentError,
333
- chatAttachmentPicking,
334
- chatAttachmentUploading,
335
- canPickChatFiles: Boolean(pickChatFilesInput),
336
- participantCount: spaceParticipants.participantCount,
337
- canChat: capabilities.canChat,
338
- canParticipants: capabilities.canParticipants,
339
- canScreenShare: capabilities.canScreenShare,
340
- screenShareUnavailableReason: features?.screenShare !== false && self.can("publishScreen") ? screenShareAvailability.detail : null,
341
- canReactions: capabilities.canReactions,
342
- canHandRaise: capabilities.canHandRaise,
343
- canInvite: capabilities.canInvite,
344
- canSettings: capabilities.canSettings,
345
- canWhiteboard: capabilities.canWhiteboard,
346
- canManageAdmission: capabilities.canManageAdmission,
347
- canRequestMedia: capabilities.canRequestMedia,
348
- canMuteParticipants: capabilities.canMuteParticipants,
349
- canRemoveParticipants: capabilities.canRemoveParticipants,
350
- canStopParticipantCamera: capabilities.canStopParticipantCamera,
351
- canStopParticipantScreenShare: capabilities.canStopParticipantScreenShare,
352
- admissionRequests: participantSlice.admissionQueue,
353
- spaceDiagnostics,
354
- participants: spaceParticipants,
355
- settings: {
356
- displayName: self.displayName ?? "",
357
- microphoneEnabled: media.local.microphone.state === "enabled",
358
- cameraEnabled: media.local.camera.state === "enabled" || media.local.camera.state === "requesting",
359
- devices: media.devices,
360
- selection: media.selection,
361
- updateDisplayName: (displayName: string) => {
362
- const nextDisplayName = displayName.trim();
363
- if (nextDisplayName) void run(() => client.participants.renameSelf(nextDisplayName));
364
- },
365
- selectMicrophone: (deviceId: string) => void run(() => client.media.selectMicrophone(deviceId)),
366
- selectCamera: (deviceId: string) => void run(() => client.media.selectCamera(deviceId)),
367
- selectSpeaker: (deviceId: string) => void run(() => client.media.selectSpeaker(deviceId)),
368
- },
369
- chat,
370
- interactions: interactionState,
371
- screenShare,
372
- layout: spacePanels.layout,
373
- whiteboard: spacePanels.whiteboard,
374
- derived,
375
- setActionsOpen,
376
- setReactionPickerOpen,
377
- setChatDraft,
378
- pickChatFiles,
379
- removeChatAttachment,
380
- handleLeave: spacePanels.handleLeave,
381
- openPanel: spacePanels.openPanel,
382
- closePanel: spacePanels.closePanel,
383
- handleInviteParticipants: spacePanels.handleInviteParticipants,
384
- toggleAudio: () => void run(() => client.media.setMicrophoneEnabled(!(media.local.microphone.state === "enabled" || media.local.microphone.state === "requesting"))),
385
- toggleVideo: () => void run(() => client.media.setCameraEnabled(!(media.local.camera.state === "enabled" || media.local.camera.state === "requesting"))),
386
- toggleScreenShare: () => void run(screenShare.toggle),
387
- toggleHand: () => void run(interactionState.toggleHand),
388
- sendReaction: (reaction: Reaction) => {
389
- if (!spaceReactions.has(reaction)) return;
390
- void run(() => interactionState.sendReaction(reaction));
391
- },
392
- sendChatMessage,
393
- resolveChatAttachmentUrl,
394
- openChatAttachment: (attachmentId: string) => {
395
- void run(async () => {
396
- const url = await resolveChatAttachmentUrl(attachmentId);
397
- if (url) await Linking.openURL(url);
398
- });
399
- },
400
- markChatMessageVisible: (sequence: string) => void run(() => chat.markAsRead(sequence)),
401
- admitParticipant: (requestId: string) => void run(() => client.participants.admit(requestId)),
402
- denyAdmission: (requestId: string) => void run(() => client.participants.deny(requestId)),
403
- removeParticipant: (participantId: string) => void run(() => client.participants.remove(participantId)),
404
- muteParticipant: (participantId: string) => void run(() => client.participants.mute(participantId)),
405
- requestUnmuteParticipant: (participantId: string) => void run(() => client.participants.requestMedia(participantId, "microphone")),
406
- requestStartParticipantCamera: (participantId: string) => void run(() => client.participants.requestMedia(participantId, "camera")),
407
- stopParticipantCamera: (participantId: string) => void run(() => client.participants.stopVideo(participantId)),
408
- stopParticipantScreenShare: (participantId: string) => void run(() => client.participants.stopScreenShare(participantId)),
409
- };
410
- }
411
-
412
- function sequenceAtOrAfter(value: string, floor: string): boolean {
413
- const normalizedValue = value.replace(/^0+(?=\d)/, "");
414
- const normalizedFloor = floor.replace(/^0+(?=\d)/, "");
415
- return normalizedValue.length > normalizedFloor.length || (normalizedValue.length === normalizedFloor.length && normalizedValue >= normalizedFloor);
416
- }
@@ -1,29 +0,0 @@
1
- import type { NativeParticipantState } from "../../ui/native-types";
2
- import { useMemo } from "react";
3
- import { useWindowDimensions } from "react-native";
4
- import { NATIVE_COMPACT_VIEWPORT_MAX_WIDTH, resolveNativeSpaceLayout, type NativeScreenShareStateLike } from "../../utils/native-space-layout";
5
-
6
- type SpaceParticipant = NativeParticipantState["participants"][number];
7
-
8
- interface UseSpaceViewDerivedOptions {
9
- participants: readonly SpaceParticipant[];
10
- localParticipant: SpaceParticipant | null;
11
- screenShare: NativeScreenShareStateLike;
12
- isWhiteboardOpen: boolean;
13
- }
14
-
15
- export function useSpaceViewDerived({ participants, localParticipant, screenShare, isWhiteboardOpen }: UseSpaceViewDerivedOptions) {
16
- const { width } = useWindowDimensions();
17
-
18
- return useMemo(
19
- () =>
20
- resolveNativeSpaceLayout({
21
- participants,
22
- localParticipant,
23
- screenShare,
24
- isWhiteboardOpen,
25
- isCompactViewport: width < NATIVE_COMPACT_VIEWPORT_MAX_WIDTH,
26
- }),
27
- [participants, localParticipant, screenShare, isWhiteboardOpen, width],
28
- );
29
- }
@@ -1,170 +0,0 @@
1
- import type { ChalkWhiteboardV1Transport } from "@q9labsai/chalk-client";
2
- import { useCallback, useEffect, useMemo, useRef, useState, type Dispatch, type SetStateAction } from "react";
3
- import { Alert, type AlertButton, Share } from "react-native";
4
-
5
- import { useLayout, type UseLayoutReturn } from "../../hooks/useLayout";
6
- import type { NativeJourneyContext, WhiteboardMetric } from "../../telemetry";
7
- import type { SpaceViewProps } from "../SpaceView";
8
- import { formatSpaceDuration } from "./format-space-duration";
9
- import type { SpaceViewActionRunner, SpacePanelName } from "./types";
10
-
11
- const emptyElements: readonly unknown[] = Object.freeze([]);
12
- const emptyParticipants: readonly string[] = Object.freeze([]);
13
-
14
- export interface SpaceWhiteboardState {
15
- readonly isOpen: boolean;
16
- readonly canDraw: boolean;
17
- readonly canClear: boolean;
18
- readonly elements: readonly unknown[];
19
- readonly openParticipants: readonly string[];
20
- readonly transport: ChalkWhiteboardV1Transport | null;
21
- readonly journeyId: string;
22
- readonly traceparent?: string;
23
- readonly tracestate?: string;
24
- readonly onMetric?: (metric: WhiteboardMetric) => void;
25
- readonly open: () => void;
26
- readonly close: () => void;
27
- readonly toggle: () => void;
28
- readonly requestSync: () => void;
29
- readonly clear: () => void;
30
- }
31
-
32
- export interface SpaceViewPanels {
33
- readonly layout: UseLayoutReturn;
34
- readonly panel: SpacePanelName | null;
35
- readonly secondsElapsed: number;
36
- readonly formattedDuration: string;
37
- readonly actionsOpen: boolean;
38
- readonly reactionPickerOpen: boolean;
39
- readonly settingsOpen: boolean;
40
- readonly whiteboard: SpaceWhiteboardState;
41
- readonly setActionsOpen: Dispatch<SetStateAction<boolean>>;
42
- readonly setReactionPickerOpen: Dispatch<SetStateAction<boolean>>;
43
- readonly handleLeave: () => void;
44
- readonly openPanel: (nextPanel: SpacePanelName) => void;
45
- readonly closePanel: () => void;
46
- readonly handleInviteParticipants: () => void;
47
- }
48
-
49
- interface UseSpaceViewPanelsOptions {
50
- readonly spaceName?: string;
51
- readonly inviteLink?: string;
52
- readonly layout?: SpaceViewProps["layout"];
53
- readonly onLayoutChange?: SpaceViewProps["onLayoutChange"];
54
- readonly canWhiteboard: boolean;
55
- readonly canDraw: boolean;
56
- readonly canClear: boolean;
57
- readonly canEndEpisode: boolean;
58
- readonly transport: ChalkWhiteboardV1Transport | null;
59
- readonly onLeave: SpaceViewProps["onLeave"];
60
- readonly onEndEpisode: SpaceViewProps["onEndEpisode"];
61
- readonly run: SpaceViewActionRunner;
62
- readonly journeyContext?: NativeJourneyContext;
63
- /** Test-fixture state only; SpaceView does not expose controlled panel state publicly. */
64
- readonly initialState?: SpaceViewFixtureState;
65
- /** Test-fixture state only; SpaceView does not expose controlled panel state publicly. */
66
- readonly controlledState?: SpaceViewFixtureState;
67
- }
68
-
69
- type SpaceViewFixtureState = {
70
- readonly layout?: SpaceViewProps["layout"];
71
- readonly panel?: SpacePanelName | null;
72
- readonly actionsOpen?: boolean;
73
- readonly reactionPickerOpen?: boolean;
74
- readonly settingsOpen?: boolean;
75
- readonly durationSeconds?: number;
76
- readonly whiteboardOpen?: boolean;
77
- };
78
-
79
- export function useSpaceViewPanels({ spaceName, inviteLink, layout: controlledLayout, onLayoutChange, canWhiteboard, canDraw, canClear, canEndEpisode, transport, onLeave, onEndEpisode, run, journeyContext, initialState, controlledState }: UseSpaceViewPanelsOptions): SpaceViewPanels {
80
- const fixtureLayout = controlledState?.layout ?? initialState?.layout ?? controlledLayout;
81
- const layout = useLayout({ layout: fixtureLayout, onLayoutChange });
82
- const [panel, setPanel] = useState<SpacePanelName | null>(initialState?.panel ?? null);
83
- const [actionsOpen, setActionsOpen] = useState(initialState?.actionsOpen ?? false);
84
- const [reactionPickerOpen, setReactionPickerOpen] = useState(initialState?.reactionPickerOpen ?? false);
85
- const [secondsElapsed, setSecondsElapsed] = useState(0);
86
- const [settingsOpen, setSettingsOpen] = useState(initialState?.settingsOpen ?? false);
87
- const [whiteboardOpen, setWhiteboardOpen] = useState(initialState?.whiteboardOpen ?? false);
88
- const generatedJourneyId = useRef(globalThis.crypto?.randomUUID?.() ?? `native-whiteboard-${Date.now()}-${Math.random().toString(36).slice(2)}`);
89
-
90
- useEffect(() => {
91
- const durationSeconds = Math.max(0, Math.floor(controlledState?.durationSeconds ?? initialState?.durationSeconds ?? 0));
92
- const startedAt = Date.now() - durationSeconds * 1_000;
93
- const update = () => setSecondsElapsed(Math.max(0, Math.floor((Date.now() - startedAt) / 1_000)));
94
- update();
95
- const timer = globalThis.setInterval(update, 1_000);
96
- return () => globalThis.clearInterval(timer);
97
- }, [controlledState?.durationSeconds, initialState?.durationSeconds]);
98
-
99
- useEffect(() => {
100
- if (controlledState?.panel !== undefined) setPanel(controlledState.panel ?? null);
101
- if (controlledState?.actionsOpen !== undefined) setActionsOpen(controlledState.actionsOpen);
102
- if (controlledState?.reactionPickerOpen !== undefined) setReactionPickerOpen(controlledState.reactionPickerOpen);
103
- if (controlledState?.settingsOpen !== undefined) setSettingsOpen(controlledState.settingsOpen);
104
- if (controlledState?.whiteboardOpen !== undefined) setWhiteboardOpen(controlledState.whiteboardOpen);
105
- }, [controlledState]);
106
-
107
- const whiteboard = useMemo<SpaceWhiteboardState>(
108
- () => ({
109
- isOpen: canWhiteboard && whiteboardOpen,
110
- canDraw: canWhiteboard && canDraw,
111
- canClear: canWhiteboard && canClear,
112
- elements: emptyElements,
113
- openParticipants: emptyParticipants,
114
- transport,
115
- journeyId: journeyContext?.journeyId ?? generatedJourneyId.current,
116
- ...(journeyContext?.traceparent ? { traceparent: journeyContext.traceparent } : {}),
117
- ...(journeyContext?.tracestate ? { tracestate: journeyContext.tracestate } : {}),
118
- ...(journeyContext ? { onMetric: journeyContext.recordWhiteboardMetric } : {}),
119
- open: () => canWhiteboard && setWhiteboardOpen(true),
120
- close: () => setWhiteboardOpen(false),
121
- toggle: () => canWhiteboard && setWhiteboardOpen((value) => !value),
122
- requestSync: () => void run(() => transport?.requestSnapshot()),
123
- clear: () => void run(() => transport?.clear()),
124
- }),
125
- [canClear, canDraw, canWhiteboard, journeyContext, run, transport, whiteboardOpen],
126
- );
127
-
128
- const handleLeave = useCallback(() => {
129
- const buttons: AlertButton[] = [
130
- { text: "Cancel", style: "cancel" },
131
- { text: "Leave", style: "destructive", onPress: () => void run(onLeave) },
132
- ];
133
- if (canEndEpisode && onEndEpisode) {
134
- buttons.splice(1, 0, {
135
- text: "End for All",
136
- style: "destructive",
137
- onPress: () => void run(onEndEpisode),
138
- });
139
- }
140
- Alert.alert("Leave Space?", "Choose how you want to leave.", buttons);
141
- }, [canEndEpisode, onEndEpisode, onLeave, run]);
142
-
143
- const handleInviteParticipants = useCallback(() => {
144
- void run(async () => {
145
- if (!inviteLink) throw new Error("The Space invite is not ready yet.");
146
- await Share.share({ message: inviteLink, title: spaceName || "Chalk Space", url: inviteLink });
147
- });
148
- }, [inviteLink, run, spaceName]);
149
-
150
- return {
151
- layout,
152
- panel,
153
- secondsElapsed,
154
- formattedDuration: formatSpaceDuration(secondsElapsed),
155
- actionsOpen,
156
- reactionPickerOpen,
157
- whiteboard,
158
- settingsOpen,
159
- setActionsOpen,
160
- setReactionPickerOpen,
161
- handleLeave,
162
- openPanel: (nextPanel: SpacePanelName) => {
163
- setActionsOpen(false);
164
- if (nextPanel === "whiteboard") whiteboard.open();
165
- else setPanel(nextPanel);
166
- },
167
- closePanel: () => setPanel(null),
168
- handleInviteParticipants,
169
- };
170
- }
@@ -1,62 +0,0 @@
1
- import type { FeedbackScreenshotCapture, FeedbackScreenshotUnavailable } from "@q9labsai/chalk-client";
2
- import { Platform, type View } from "react-native";
3
- import { captureRef } from "react-native-view-shot";
4
-
5
- export type FeedbackCaptureTarget = React.RefObject<View | null>;
6
-
7
- export type FeedbackCaptureSize = Readonly<{
8
- width: number;
9
- height: number;
10
- }>;
11
-
12
- const MAX_SCREENSHOT_BYTES = 450 * 1024;
13
- const MAX_SCREENSHOT_WIDTH = 1_920;
14
- const MAX_SCREENSHOT_HEIGHT = 1_080;
15
-
16
- export async function captureNativeFeedbackView(target: FeedbackCaptureTarget, size: FeedbackCaptureSize): Promise<FeedbackScreenshotCapture | FeedbackScreenshotUnavailable> {
17
- if (Platform.OS === "macos") return { state: "unavailable", failure_code: "unsupported" };
18
- if (!target.current) return { state: "unavailable", failure_code: "capture_failed" };
19
-
20
- const outputSize = boundedOutputSize(size);
21
- if (!outputSize) return { state: "unavailable", failure_code: "capture_failed" };
22
- try {
23
- const captured = await captureRef(target, {
24
- format: "jpg",
25
- quality: 0.72,
26
- result: "base64",
27
- ...outputSize,
28
- });
29
- const dataBase64 = normalizeBase64(captured);
30
- if (!dataBase64) return { state: "unavailable", failure_code: "capture_failed" };
31
- if (base64ByteLength(dataBase64) > MAX_SCREENSHOT_BYTES) return { state: "unavailable", failure_code: "too_large" };
32
-
33
- return {
34
- state: "captured",
35
- mime_type: "image/jpeg",
36
- width: outputSize.width,
37
- height: outputSize.height,
38
- captured_at: new Date().toISOString(),
39
- data_base64: dataBase64,
40
- };
41
- } catch {
42
- return { state: "unavailable", failure_code: "capture_failed" };
43
- }
44
- }
45
-
46
- function boundedOutputSize(size: FeedbackCaptureSize): Readonly<{ width: number; height: number }> | undefined {
47
- if (!Number.isFinite(size.width) || !Number.isFinite(size.height) || size.width <= 0 || size.height <= 0) return undefined;
48
- const scale = Math.min(1, MAX_SCREENSHOT_WIDTH / size.width, MAX_SCREENSHOT_HEIGHT / size.height);
49
- return {
50
- width: Math.max(1, Math.floor(size.width * scale)),
51
- height: Math.max(1, Math.floor(size.height * scale)),
52
- };
53
- }
54
-
55
- function normalizeBase64(value: string): string {
56
- return value.replace(/^data:[^;]+;base64,/u, "").replaceAll(/\s/gu, "");
57
- }
58
-
59
- function base64ByteLength(value: string): number {
60
- const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
61
- return Math.floor((value.length * 3) / 4) - padding;
62
- }