@dxos/plugin-thread 0.8.2-staging.7ac8446 → 0.8.2

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 (407) hide show
  1. package/dist/lib/browser/ChannelContainer-LGYSQMC7.mjs +15 -0
  2. package/dist/lib/browser/ChatContainer-JWRI73OG.mjs +14 -0
  3. package/dist/lib/browser/app-graph-builder-K3CFBN7V.mjs +171 -0
  4. package/dist/lib/browser/app-graph-builder-K3CFBN7V.mjs.map +7 -0
  5. package/dist/lib/browser/call-manager-APQAZWHT.mjs +23 -0
  6. package/dist/lib/browser/call-manager-APQAZWHT.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-4H4A264L.mjs +1708 -0
  8. package/dist/lib/browser/chunk-4H4A264L.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-QDJ3Y6WF.mjs → chunk-5P5BH5GE.mjs} +57 -35
  10. package/dist/lib/browser/chunk-5P5BH5GE.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-5PGLGDIP.mjs +983 -0
  12. package/dist/lib/browser/chunk-5PGLGDIP.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-CF2MXRCH.mjs +21 -0
  14. package/dist/lib/browser/chunk-CF2MXRCH.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-G3TS64FP.mjs +351 -0
  16. package/dist/lib/browser/chunk-G3TS64FP.mjs.map +7 -0
  17. package/dist/lib/browser/{chunk-WBJLUUOK.mjs → chunk-IIUTBDVB.mjs} +4 -4
  18. package/dist/lib/browser/chunk-IIUTBDVB.mjs.map +7 -0
  19. package/dist/lib/browser/chunk-JKQUJOTX.mjs +31 -0
  20. package/dist/lib/browser/chunk-JKQUJOTX.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-KMVGJG23.mjs +545 -0
  22. package/dist/lib/browser/chunk-KMVGJG23.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-EP6MRLY7.mjs → chunk-R5MDLUPC.mjs} +5 -3
  24. package/dist/lib/browser/chunk-R5MDLUPC.mjs.map +7 -0
  25. package/dist/lib/browser/chunk-WDOQXTI2.mjs +126 -0
  26. package/dist/lib/browser/chunk-WDOQXTI2.mjs.map +7 -0
  27. package/dist/lib/browser/index.mjs +107 -41
  28. package/dist/lib/browser/index.mjs.map +4 -4
  29. package/dist/lib/browser/{intent-resolver-QAY4FLLM.mjs → intent-resolver-YUYZCTLF.mjs} +102 -74
  30. package/dist/lib/browser/intent-resolver-YUYZCTLF.mjs.map +7 -0
  31. package/dist/lib/browser/markdown-4EC3OCF2.mjs +169 -0
  32. package/dist/lib/browser/markdown-4EC3OCF2.mjs.map +7 -0
  33. package/dist/lib/browser/meta.json +1 -1
  34. package/dist/lib/browser/react-root-EECCWHCW.mjs +27 -0
  35. package/dist/lib/browser/react-root-EECCWHCW.mjs.map +7 -0
  36. package/dist/lib/browser/react-surface-5QLZAFDL.mjs +108 -0
  37. package/dist/lib/browser/react-surface-5QLZAFDL.mjs.map +7 -0
  38. package/dist/lib/browser/{settings-4FEC7EAX.mjs → settings-KVBQQSO5.mjs} +5 -5
  39. package/dist/lib/browser/settings-KVBQQSO5.mjs.map +7 -0
  40. package/dist/lib/browser/{state-PNMSLKAH.mjs → state-RKL5MSDP.mjs} +7 -7
  41. package/dist/lib/browser/state-RKL5MSDP.mjs.map +7 -0
  42. package/dist/lib/browser/types/index.mjs +16 -0
  43. package/dist/lib/browser/types/index.mjs.map +7 -0
  44. package/dist/lib/node/ChannelContainer-5EKNEH56.cjs +36 -0
  45. package/dist/lib/node/ChannelContainer-5EKNEH56.cjs.map +7 -0
  46. package/dist/lib/node/ChatContainer-EE3FPSYO.cjs +35 -0
  47. package/dist/lib/node/ChatContainer-EE3FPSYO.cjs.map +7 -0
  48. package/dist/lib/node/app-graph-builder-IRE3GYKE.cjs +179 -0
  49. package/dist/lib/node/app-graph-builder-IRE3GYKE.cjs.map +7 -0
  50. package/dist/lib/node/call-manager-HV2XJW5O.cjs +37 -0
  51. package/dist/lib/node/call-manager-HV2XJW5O.cjs.map +7 -0
  52. package/dist/lib/node/chunk-44I33JDT.cjs +1731 -0
  53. package/dist/lib/node/chunk-44I33JDT.cjs.map +7 -0
  54. package/dist/lib/node/chunk-4B3PPXA6.cjs +371 -0
  55. package/dist/lib/node/chunk-4B3PPXA6.cjs.map +7 -0
  56. package/dist/lib/node/{chunk-NQ5DTPOY.cjs → chunk-4YSNVPWU.cjs} +10 -8
  57. package/dist/lib/node/chunk-4YSNVPWU.cjs.map +7 -0
  58. package/dist/lib/node/chunk-6B3M6XRE.cjs +984 -0
  59. package/dist/lib/node/chunk-6B3M6XRE.cjs.map +7 -0
  60. package/dist/lib/node/{chunk-AKINTFBH.cjs → chunk-FUXJAVFZ.cjs} +50 -29
  61. package/dist/lib/node/chunk-FUXJAVFZ.cjs.map +7 -0
  62. package/dist/lib/node/{chunk-ONLWOUYZ.cjs → chunk-HLMCEE5K.cjs} +7 -7
  63. package/dist/lib/node/{chunk-ONLWOUYZ.cjs.map → chunk-HLMCEE5K.cjs.map} +2 -2
  64. package/dist/lib/node/chunk-JLHENFGK.cjs +549 -0
  65. package/dist/lib/node/chunk-JLHENFGK.cjs.map +7 -0
  66. package/dist/lib/node/chunk-LR5NENBE.cjs +54 -0
  67. package/dist/lib/node/chunk-LR5NENBE.cjs.map +7 -0
  68. package/dist/lib/node/chunk-TKHJVC2C.cjs +147 -0
  69. package/dist/lib/node/chunk-TKHJVC2C.cjs.map +7 -0
  70. package/dist/lib/node/{chunk-S2A6BXEA.cjs → chunk-U7NOIXN6.cjs} +16 -10
  71. package/dist/lib/node/chunk-U7NOIXN6.cjs.map +7 -0
  72. package/dist/lib/node/index.cjs +142 -80
  73. package/dist/lib/node/index.cjs.map +4 -4
  74. package/dist/lib/node/{intent-resolver-S6FE5OR7.cjs → intent-resolver-MZ3HADNY.cjs} +108 -82
  75. package/dist/lib/node/intent-resolver-MZ3HADNY.cjs.map +7 -0
  76. package/dist/lib/node/markdown-NJDPXIKD.cjs +178 -0
  77. package/dist/lib/node/markdown-NJDPXIKD.cjs.map +7 -0
  78. package/dist/lib/node/meta.json +1 -1
  79. package/dist/lib/node/react-root-IAYQ2DBO.cjs +51 -0
  80. package/dist/lib/node/react-root-IAYQ2DBO.cjs.map +7 -0
  81. package/dist/lib/node/react-surface-AVC5VVIS.cjs +122 -0
  82. package/dist/lib/node/react-surface-AVC5VVIS.cjs.map +7 -0
  83. package/dist/lib/node/{settings-IXCPE55J.cjs → settings-H45P5YO2.cjs} +9 -9
  84. package/dist/lib/node/settings-H45P5YO2.cjs.map +7 -0
  85. package/dist/lib/node/{state-AT73KPGZ.cjs → state-IY5XX3H2.cjs} +10 -10
  86. package/dist/lib/node/state-IY5XX3H2.cjs.map +7 -0
  87. package/dist/lib/node/{types.cjs → types/index.cjs} +12 -6
  88. package/dist/lib/node/types/index.cjs.map +7 -0
  89. package/dist/lib/node-esm/ChannelContainer-KFS2RWWW.mjs +16 -0
  90. package/dist/lib/node-esm/ChannelContainer-KFS2RWWW.mjs.map +7 -0
  91. package/dist/lib/node-esm/ChatContainer-UJYPQMLV.mjs +15 -0
  92. package/dist/lib/node-esm/ChatContainer-UJYPQMLV.mjs.map +7 -0
  93. package/dist/lib/node-esm/app-graph-builder-I5VMPLE4.mjs +172 -0
  94. package/dist/lib/node-esm/app-graph-builder-I5VMPLE4.mjs.map +7 -0
  95. package/dist/lib/node-esm/call-manager-UUYXLF4T.mjs +24 -0
  96. package/dist/lib/node-esm/call-manager-UUYXLF4T.mjs.map +7 -0
  97. package/dist/lib/node-esm/{chunk-42F2DTMM.mjs → chunk-ESQC6SYE.mjs} +4 -4
  98. package/dist/lib/node-esm/chunk-ESQC6SYE.mjs.map +7 -0
  99. package/dist/lib/node-esm/chunk-J323VQ4P.mjs +127 -0
  100. package/dist/lib/node-esm/chunk-J323VQ4P.mjs.map +7 -0
  101. package/dist/lib/node-esm/{chunk-WLY2HMDB.mjs → chunk-K4KAUOKT.mjs} +5 -3
  102. package/dist/lib/node-esm/chunk-K4KAUOKT.mjs.map +7 -0
  103. package/dist/lib/node-esm/chunk-MNJ3V4XM.mjs +546 -0
  104. package/dist/lib/node-esm/chunk-MNJ3V4XM.mjs.map +7 -0
  105. package/dist/lib/node-esm/{chunk-J56FORYF.mjs → chunk-O3V45YJP.mjs} +57 -35
  106. package/dist/lib/node-esm/chunk-O3V45YJP.mjs.map +7 -0
  107. package/dist/lib/node-esm/chunk-ODUB4GAB.mjs +23 -0
  108. package/dist/lib/node-esm/chunk-ODUB4GAB.mjs.map +7 -0
  109. package/dist/lib/node-esm/chunk-PGRP3O4O.mjs +33 -0
  110. package/dist/lib/node-esm/chunk-PGRP3O4O.mjs.map +7 -0
  111. package/dist/lib/node-esm/chunk-POKXRTII.mjs +352 -0
  112. package/dist/lib/node-esm/chunk-POKXRTII.mjs.map +7 -0
  113. package/dist/lib/node-esm/chunk-WXNPD542.mjs +1710 -0
  114. package/dist/lib/node-esm/chunk-WXNPD542.mjs.map +7 -0
  115. package/dist/lib/node-esm/chunk-X6AUQDTE.mjs +984 -0
  116. package/dist/lib/node-esm/chunk-X6AUQDTE.mjs.map +7 -0
  117. package/dist/lib/node-esm/index.mjs +107 -41
  118. package/dist/lib/node-esm/index.mjs.map +4 -4
  119. package/dist/lib/node-esm/{intent-resolver-OURFRSSF.mjs → intent-resolver-CU22OPAH.mjs} +102 -74
  120. package/dist/lib/node-esm/intent-resolver-CU22OPAH.mjs.map +7 -0
  121. package/dist/lib/node-esm/markdown-HRGGO7IA.mjs +170 -0
  122. package/dist/lib/node-esm/markdown-HRGGO7IA.mjs.map +7 -0
  123. package/dist/lib/node-esm/meta.json +1 -1
  124. package/dist/lib/node-esm/react-root-NQMUEI6Z.mjs +28 -0
  125. package/dist/lib/node-esm/react-root-NQMUEI6Z.mjs.map +7 -0
  126. package/dist/lib/node-esm/react-surface-Z4X5VLMY.mjs +109 -0
  127. package/dist/lib/node-esm/react-surface-Z4X5VLMY.mjs.map +7 -0
  128. package/dist/lib/node-esm/{settings-ZUJQ4LWI.mjs → settings-IJGUOU3F.mjs} +5 -5
  129. package/dist/lib/node-esm/settings-IJGUOU3F.mjs.map +7 -0
  130. package/dist/lib/node-esm/{state-SSDX5O67.mjs → state-6AUTNROZ.mjs} +7 -7
  131. package/dist/lib/node-esm/state-6AUTNROZ.mjs.map +7 -0
  132. package/dist/lib/node-esm/types/index.mjs +17 -0
  133. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  134. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  135. package/dist/types/src/calls/call-manager.d.ts +77 -0
  136. package/dist/types/src/calls/call-manager.d.ts.map +1 -0
  137. package/dist/types/src/calls/call-swarm-synchronizer.d.ts +66 -0
  138. package/dist/types/src/calls/call-swarm-synchronizer.d.ts.map +1 -0
  139. package/dist/types/src/calls/index.d.ts +7 -0
  140. package/dist/types/src/calls/index.d.ts.map +1 -0
  141. package/dist/types/src/calls/media-manager.d.ts +60 -0
  142. package/dist/types/src/calls/media-manager.d.ts.map +1 -0
  143. package/dist/types/src/calls/speaking-monitor.d.ts +24 -0
  144. package/dist/types/src/calls/speaking-monitor.d.ts.map +1 -0
  145. package/dist/types/src/calls/types.d.ts +45 -0
  146. package/dist/types/src/calls/types.d.ts.map +1 -0
  147. package/dist/types/src/calls/util/audio.d.ts +6 -0
  148. package/dist/types/src/calls/util/audio.d.ts.map +1 -0
  149. package/dist/types/src/calls/util/calls-service.d.ts +72 -0
  150. package/dist/types/src/calls/util/calls-service.d.ts.map +1 -0
  151. package/dist/types/src/calls/util/history-cache.d.ts +11 -0
  152. package/dist/types/src/calls/util/history-cache.d.ts.map +1 -0
  153. package/dist/types/src/calls/util/index.d.ts +5 -0
  154. package/dist/types/src/calls/util/index.d.ts.map +1 -0
  155. package/dist/types/src/calls/util/media.d.ts +6 -0
  156. package/dist/types/src/calls/util/media.d.ts.map +1 -0
  157. package/dist/types/src/calls/util/stub-media-streams.d.ts +18 -0
  158. package/dist/types/src/calls/util/stub-media-streams.d.ts.map +1 -0
  159. package/dist/types/src/calls/util/task-scheduling.d.ts +17 -0
  160. package/dist/types/src/calls/util/task-scheduling.d.ts.map +1 -0
  161. package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -179
  162. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  163. package/dist/types/src/capabilities/call-manager.d.ts +5 -0
  164. package/dist/types/src/capabilities/call-manager.d.ts.map +1 -0
  165. package/dist/types/src/capabilities/capabilities.d.ts +13 -1
  166. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  167. package/dist/types/src/capabilities/index.d.ts +8 -179
  168. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  169. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  170. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  171. package/dist/types/src/capabilities/markdown.d.ts +2 -2
  172. package/dist/types/src/capabilities/markdown.d.ts.map +1 -1
  173. package/dist/types/src/capabilities/react-root.d.ts +7 -0
  174. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  175. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  176. package/dist/types/src/components/Call/Call.d.ts +17 -0
  177. package/dist/types/src/components/Call/Call.d.ts.map +1 -0
  178. package/dist/types/src/components/Call/Toolbar.d.ts +16 -0
  179. package/dist/types/src/components/Call/Toolbar.d.ts.map +1 -0
  180. package/dist/types/src/components/Call/index.d.ts +3 -0
  181. package/dist/types/src/components/Call/index.d.ts.map +1 -0
  182. package/dist/types/src/components/CallDebugPanel.d.ts +8 -0
  183. package/dist/types/src/components/CallDebugPanel.d.ts.map +1 -0
  184. package/dist/types/src/components/CallSidebar.d.ts +4 -0
  185. package/dist/types/src/components/CallSidebar.d.ts.map +1 -0
  186. package/dist/types/src/components/ChannelContainer.d.ts +14 -0
  187. package/dist/types/src/components/ChannelContainer.d.ts.map +1 -0
  188. package/dist/types/src/components/ChannelContainer.stories.d.ts +10 -0
  189. package/dist/types/src/components/ChannelContainer.stories.d.ts.map +1 -0
  190. package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
  191. package/dist/types/src/components/ChatContainer.d.ts +9 -7
  192. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  193. package/dist/types/src/components/CommentContainer.d.ts +11 -2
  194. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  195. package/dist/types/src/components/Comments.stories.d.ts +1 -1
  196. package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
  197. package/dist/types/src/components/CommentsContainer.d.ts +5 -9
  198. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  199. package/dist/types/src/components/Lobby/Lobby.d.ts +15 -0
  200. package/dist/types/src/components/Lobby/Lobby.d.ts.map +1 -0
  201. package/dist/types/src/components/Lobby/index.d.ts +2 -0
  202. package/dist/types/src/components/Lobby/index.d.ts.map +1 -0
  203. package/dist/types/src/components/Media/AudioStream.d.ts +6 -0
  204. package/dist/types/src/components/Media/AudioStream.d.ts.map +1 -0
  205. package/dist/types/src/components/Media/VideoObject.d.ts +15 -0
  206. package/dist/types/src/components/Media/VideoObject.d.ts.map +1 -0
  207. package/dist/types/src/components/Media/VideoObject.stories.d.ts +8 -0
  208. package/dist/types/src/components/Media/VideoObject.stories.d.ts.map +1 -0
  209. package/dist/types/src/components/Media/index.d.ts +3 -0
  210. package/dist/types/src/components/Media/index.d.ts.map +1 -0
  211. package/dist/types/src/components/MessageContainer.d.ts +5 -4
  212. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  213. package/dist/types/src/components/Participant/Participant.d.ts +6 -0
  214. package/dist/types/src/components/Participant/Participant.d.ts.map +1 -0
  215. package/dist/types/src/components/Participant/ParticipantGrid.d.ts +11 -0
  216. package/dist/types/src/components/Participant/ParticipantGrid.d.ts.map +1 -0
  217. package/dist/types/src/components/Participant/index.d.ts +3 -0
  218. package/dist/types/src/components/Participant/index.d.ts.map +1 -0
  219. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.d.ts +12 -0
  220. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.d.ts.map +1 -0
  221. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts +10 -0
  222. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts.map +1 -0
  223. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.d.ts +32 -0
  224. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.d.ts.map +1 -0
  225. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts +23 -0
  226. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts.map +1 -0
  227. package/dist/types/src/components/ResponsiveGrid/ResponsiveGridItem.d.ts +21 -0
  228. package/dist/types/src/components/ResponsiveGrid/ResponsiveGridItem.d.ts.map +1 -0
  229. package/dist/types/src/components/ResponsiveGrid/index.d.ts +4 -0
  230. package/dist/types/src/components/ResponsiveGrid/index.d.ts.map +1 -0
  231. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
  232. package/dist/types/src/components/ThreadSettings.d.ts.map +1 -1
  233. package/dist/types/src/components/index.d.ts +5 -1
  234. package/dist/types/src/components/index.d.ts.map +1 -1
  235. package/dist/types/src/components/testing.d.ts +101 -54
  236. package/dist/types/src/components/testing.d.ts.map +1 -1
  237. package/dist/types/src/extensions/threads.d.ts.map +1 -1
  238. package/dist/types/src/hooks/analytics.d.ts +3 -3
  239. package/dist/types/src/hooks/analytics.d.ts.map +1 -1
  240. package/dist/types/src/hooks/index.d.ts +2 -1
  241. package/dist/types/src/hooks/index.d.ts.map +1 -1
  242. package/dist/types/src/hooks/useDebugMode.d.ts +2 -0
  243. package/dist/types/src/hooks/useDebugMode.d.ts.map +1 -0
  244. package/dist/types/src/hooks/useStatus.d.ts.map +1 -1
  245. package/dist/types/src/index.d.ts +1 -1
  246. package/dist/types/src/index.d.ts.map +1 -1
  247. package/dist/types/src/meta.d.ts.map +1 -1
  248. package/dist/types/src/peers.stories.d.ts +11 -0
  249. package/dist/types/src/peers.stories.d.ts.map +1 -0
  250. package/dist/types/src/testing/index.d.ts +2 -0
  251. package/dist/types/src/testing/index.d.ts.map +1 -0
  252. package/dist/types/src/testing/stories.d.ts +2 -0
  253. package/dist/types/src/testing/stories.d.ts.map +1 -0
  254. package/dist/types/src/translations.d.ts +35 -56
  255. package/dist/types/src/translations.d.ts.map +1 -1
  256. package/dist/types/src/types/index.d.ts +3 -0
  257. package/dist/types/src/types/index.d.ts.map +1 -0
  258. package/dist/types/src/types/schema.d.ts +317 -0
  259. package/dist/types/src/types/schema.d.ts.map +1 -0
  260. package/dist/types/src/types/types.d.ts +1015 -0
  261. package/dist/types/src/types/types.d.ts.map +1 -0
  262. package/dist/types/src/util.d.ts +2 -0
  263. package/dist/types/src/util.d.ts.map +1 -1
  264. package/dist/types/tsconfig.tsbuildinfo +1 -1
  265. package/package.json +60 -40
  266. package/src/ThreadPlugin.tsx +33 -14
  267. package/src/calls/call-manager.ts +242 -0
  268. package/src/calls/call-swarm-synchronizer.ts +350 -0
  269. package/src/calls/index.ts +10 -0
  270. package/src/calls/media-manager.ts +342 -0
  271. package/src/calls/speaking-monitor.ts +85 -0
  272. package/src/calls/types.ts +70 -0
  273. package/src/calls/util/audio.ts +58 -0
  274. package/src/calls/util/calls-service.ts +527 -0
  275. package/src/calls/util/history-cache.ts +27 -0
  276. package/src/calls/util/index.ts +8 -0
  277. package/src/calls/util/media.ts +52 -0
  278. package/src/calls/util/stub-media-streams.ts +73 -0
  279. package/src/calls/util/task-scheduling.ts +92 -0
  280. package/src/capabilities/app-graph-builder.ts +171 -25
  281. package/src/capabilities/call-manager.ts +19 -0
  282. package/src/capabilities/capabilities.ts +15 -1
  283. package/src/capabilities/index.ts +2 -0
  284. package/src/capabilities/intent-resolver.ts +77 -56
  285. package/src/capabilities/markdown.ts +54 -4
  286. package/src/capabilities/react-root.tsx +17 -0
  287. package/src/capabilities/react-surface.tsx +52 -27
  288. package/src/capabilities/settings.ts +2 -2
  289. package/src/capabilities/state.ts +3 -3
  290. package/src/components/Call/Call.tsx +88 -0
  291. package/src/components/Call/Toolbar.tsx +182 -0
  292. package/src/components/Call/index.ts +6 -0
  293. package/src/components/CallDebugPanel.tsx +115 -0
  294. package/src/components/CallSidebar.tsx +17 -0
  295. package/src/components/ChannelContainer.stories.tsx +58 -0
  296. package/src/components/ChannelContainer.tsx +203 -0
  297. package/src/components/Chat.stories.tsx +11 -12
  298. package/src/components/ChatContainer.tsx +37 -25
  299. package/src/components/CommentContainer.tsx +56 -61
  300. package/src/components/Comments.stories.tsx +20 -16
  301. package/src/components/CommentsContainer.tsx +20 -36
  302. package/src/components/Lobby/Lobby.tsx +119 -0
  303. package/src/components/Lobby/index.ts +5 -0
  304. package/src/components/Media/AudioStream.tsx +67 -0
  305. package/src/components/Media/VideoObject.stories.tsx +52 -0
  306. package/src/components/Media/VideoObject.tsx +58 -0
  307. package/src/components/Media/index.ts +6 -0
  308. package/src/components/MessageContainer.tsx +31 -50
  309. package/src/components/Participant/Participant.tsx +57 -0
  310. package/src/components/Participant/ParticipantGrid.tsx +88 -0
  311. package/src/components/Participant/index.ts +6 -0
  312. package/src/components/ResponsiveGrid/ResponsiveContainer.stories.tsx +54 -0
  313. package/src/components/ResponsiveGrid/ResponsiveContainer.tsx +31 -0
  314. package/src/components/ResponsiveGrid/ResponsiveGrid.stories.tsx +156 -0
  315. package/src/components/ResponsiveGrid/ResponsiveGrid.tsx +341 -0
  316. package/src/components/ResponsiveGrid/ResponsiveGridItem.tsx +131 -0
  317. package/src/components/ResponsiveGrid/index.ts +7 -0
  318. package/src/components/ThreadComplementary.tsx +37 -42
  319. package/src/components/index.ts +4 -0
  320. package/src/components/testing.ts +6 -5
  321. package/src/extensions/threads.ts +42 -24
  322. package/src/hooks/analytics.ts +4 -4
  323. package/src/hooks/index.ts +2 -1
  324. package/src/hooks/useDebugMode.ts +25 -0
  325. package/src/index.ts +1 -1
  326. package/src/meta.ts +4 -3
  327. package/src/peers.stories.tsx +189 -0
  328. package/src/testing/index.ts +5 -0
  329. package/src/testing/stories.ts +50 -0
  330. package/src/translations.ts +46 -5
  331. package/src/types/index.ts +6 -0
  332. package/src/types/schema.ts +29 -0
  333. package/src/types/types.ts +110 -0
  334. package/src/typings/peermetrics-webrtc-stats.d.ts +30 -0
  335. package/src/util.ts +12 -0
  336. package/dist/lib/browser/ChatContainer-IOOAQW6S.mjs +0 -138
  337. package/dist/lib/browser/ChatContainer-IOOAQW6S.mjs.map +0 -7
  338. package/dist/lib/browser/app-graph-builder-6W4UKJDI.mjs +0 -41
  339. package/dist/lib/browser/app-graph-builder-6W4UKJDI.mjs.map +0 -7
  340. package/dist/lib/browser/chunk-EP6MRLY7.mjs.map +0 -7
  341. package/dist/lib/browser/chunk-QDJ3Y6WF.mjs.map +0 -7
  342. package/dist/lib/browser/chunk-S5ZR4FYW.mjs +0 -17
  343. package/dist/lib/browser/chunk-S5ZR4FYW.mjs.map +0 -7
  344. package/dist/lib/browser/chunk-TFVPUPOO.mjs +0 -207
  345. package/dist/lib/browser/chunk-TFVPUPOO.mjs.map +0 -7
  346. package/dist/lib/browser/chunk-WBJLUUOK.mjs.map +0 -7
  347. package/dist/lib/browser/chunk-WOG72DWE.mjs +0 -93
  348. package/dist/lib/browser/chunk-WOG72DWE.mjs.map +0 -7
  349. package/dist/lib/browser/intent-resolver-QAY4FLLM.mjs.map +0 -7
  350. package/dist/lib/browser/markdown-7KXBXCUS.mjs +0 -111
  351. package/dist/lib/browser/markdown-7KXBXCUS.mjs.map +0 -7
  352. package/dist/lib/browser/react-surface-MT3OWULR.mjs +0 -437
  353. package/dist/lib/browser/react-surface-MT3OWULR.mjs.map +0 -7
  354. package/dist/lib/browser/settings-4FEC7EAX.mjs.map +0 -7
  355. package/dist/lib/browser/state-PNMSLKAH.mjs.map +0 -7
  356. package/dist/lib/browser/types.mjs +0 -10
  357. package/dist/lib/node/ChatContainer-4FNDRF3G.cjs +0 -163
  358. package/dist/lib/node/ChatContainer-4FNDRF3G.cjs.map +0 -7
  359. package/dist/lib/node/app-graph-builder-HZNWT4QU.cjs +0 -57
  360. package/dist/lib/node/app-graph-builder-HZNWT4QU.cjs.map +0 -7
  361. package/dist/lib/node/chunk-46BSFPBV.cjs +0 -113
  362. package/dist/lib/node/chunk-46BSFPBV.cjs.map +0 -7
  363. package/dist/lib/node/chunk-AKINTFBH.cjs.map +0 -7
  364. package/dist/lib/node/chunk-E7Z3BKRQ.cjs +0 -232
  365. package/dist/lib/node/chunk-E7Z3BKRQ.cjs.map +0 -7
  366. package/dist/lib/node/chunk-NQ5DTPOY.cjs.map +0 -7
  367. package/dist/lib/node/chunk-S2A6BXEA.cjs.map +0 -7
  368. package/dist/lib/node/intent-resolver-S6FE5OR7.cjs.map +0 -7
  369. package/dist/lib/node/markdown-L2WMCQKM.cjs +0 -121
  370. package/dist/lib/node/markdown-L2WMCQKM.cjs.map +0 -7
  371. package/dist/lib/node/react-surface-Z45BYECE.cjs +0 -439
  372. package/dist/lib/node/react-surface-Z45BYECE.cjs.map +0 -7
  373. package/dist/lib/node/settings-IXCPE55J.cjs.map +0 -7
  374. package/dist/lib/node/state-AT73KPGZ.cjs.map +0 -7
  375. package/dist/lib/node/types.cjs.map +0 -7
  376. package/dist/lib/node-esm/ChatContainer-NYLCL6QA.mjs +0 -139
  377. package/dist/lib/node-esm/ChatContainer-NYLCL6QA.mjs.map +0 -7
  378. package/dist/lib/node-esm/app-graph-builder-M3KK5QJH.mjs +0 -42
  379. package/dist/lib/node-esm/app-graph-builder-M3KK5QJH.mjs.map +0 -7
  380. package/dist/lib/node-esm/chunk-42F2DTMM.mjs.map +0 -7
  381. package/dist/lib/node-esm/chunk-ADTVMESP.mjs +0 -208
  382. package/dist/lib/node-esm/chunk-ADTVMESP.mjs.map +0 -7
  383. package/dist/lib/node-esm/chunk-E446MGLP.mjs +0 -94
  384. package/dist/lib/node-esm/chunk-E446MGLP.mjs.map +0 -7
  385. package/dist/lib/node-esm/chunk-J56FORYF.mjs.map +0 -7
  386. package/dist/lib/node-esm/chunk-WLY2HMDB.mjs.map +0 -7
  387. package/dist/lib/node-esm/chunk-WSE23GSG.mjs +0 -19
  388. package/dist/lib/node-esm/chunk-WSE23GSG.mjs.map +0 -7
  389. package/dist/lib/node-esm/intent-resolver-OURFRSSF.mjs.map +0 -7
  390. package/dist/lib/node-esm/markdown-ZKU23UVD.mjs +0 -112
  391. package/dist/lib/node-esm/markdown-ZKU23UVD.mjs.map +0 -7
  392. package/dist/lib/node-esm/react-surface-KIJ767JK.mjs +0 -438
  393. package/dist/lib/node-esm/react-surface-KIJ767JK.mjs.map +0 -7
  394. package/dist/lib/node-esm/settings-ZUJQ4LWI.mjs.map +0 -7
  395. package/dist/lib/node-esm/state-SSDX5O67.mjs.map +0 -7
  396. package/dist/lib/node-esm/types.mjs +0 -11
  397. package/dist/types/src/components/types.d.ts +0 -28
  398. package/dist/types/src/components/types.d.ts.map +0 -1
  399. package/dist/types/src/events.d.ts +0 -4
  400. package/dist/types/src/events.d.ts.map +0 -1
  401. package/dist/types/src/types.d.ts +0 -125
  402. package/dist/types/src/types.d.ts.map +0 -1
  403. package/src/components/types.ts +0 -32
  404. package/src/events.ts +0 -11
  405. package/src/types.ts +0 -100
  406. /package/dist/lib/browser/{types.mjs.map → ChannelContainer-LGYSQMC7.mjs.map} +0 -0
  407. /package/dist/lib/{node-esm/types.mjs.map → browser/ChatContainer-JWRI73OG.mjs.map} +0 -0
@@ -0,0 +1,527 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { PersistentLifecycle } from '@dxos/async';
6
+ import { type Context, Resource } from '@dxos/context';
7
+ import { invariant } from '@dxos/invariant';
8
+ import { log } from '@dxos/log';
9
+
10
+ import { HistoryCache } from './history-cache';
11
+ import { BulkRequestDispatcher, FIFOScheduler } from './task-scheduling';
12
+ import {
13
+ type SessionResponse,
14
+ type ErrorResponse,
15
+ type RenegotiationResponse,
16
+ type TrackObject,
17
+ type TracksResponse,
18
+ } from '../types';
19
+
20
+ const NETWORK_TIMEOUT = 5_000;
21
+
22
+ export type Session = {
23
+ sessionId: string;
24
+ peerConnection: RTCPeerConnection;
25
+ };
26
+
27
+ export type CallsServiceConfig = {
28
+ apiBase: string;
29
+ apiExtraParams?: string;
30
+ iceServers?: RTCIceServer[];
31
+ };
32
+
33
+ export type HistoryRecord =
34
+ | {
35
+ type: 'request';
36
+ time: string;
37
+ method: string;
38
+ body: any;
39
+ }
40
+ | {
41
+ type: 'response';
42
+ time: string;
43
+ status: number;
44
+ text?: string;
45
+ json?: any;
46
+ };
47
+
48
+ /**
49
+ * Client for Cloudflare Calls service.
50
+ * API: https://developers.cloudflare.com/calls/https-api/
51
+ * Inspired by client from https://github.com/threepointone/partyserver/tree/main/packages/partytracks
52
+ */
53
+ // TODO(mykola): Expose session errors.
54
+ export class CallsServicePeer extends Resource {
55
+ public readonly history = new HistoryCache<HistoryRecord>(100);
56
+
57
+ private readonly _persistentLifecycle = new PersistentLifecycle<Session>({
58
+ start: () => this._startSession(),
59
+ stop: (session) => this._stopSession(session),
60
+ });
61
+
62
+ private readonly _config: CallsServiceConfig;
63
+
64
+ taskScheduler = new FIFOScheduler();
65
+ pushTrackDispatcher = new BulkRequestDispatcher<
66
+ {
67
+ trackName: string;
68
+ transceiver: RTCRtpTransceiver;
69
+ },
70
+ { tracks: TrackObject[] }
71
+ >(32);
72
+
73
+ pullTrackDispatcher = new BulkRequestDispatcher<
74
+ TrackObject,
75
+ {
76
+ trackMap: Map<TrackObject, { resolvedTrack: Promise<MediaStreamTrack>; mid: string }>;
77
+ }
78
+ >(32);
79
+
80
+ closeTrackDispatcher = new BulkRequestDispatcher(32);
81
+ constructor(config: CallsServiceConfig) {
82
+ super();
83
+ this._config = config;
84
+ }
85
+
86
+ protected override async _open(ctx: Context): Promise<void> {
87
+ await this._persistentLifecycle.open(ctx);
88
+ }
89
+
90
+ protected override async _close(): Promise<void> {
91
+ await this._persistentLifecycle.close();
92
+ }
93
+
94
+ get session() {
95
+ return this._persistentLifecycle.state;
96
+ }
97
+
98
+ private async _startSession() {
99
+ const peerConnection = await this._createRtcConnection();
100
+ const sessionId = await this._initiateCallSession();
101
+ return { sessionId, peerConnection };
102
+ }
103
+
104
+ private async _stopSession(session: Session): Promise<void> {
105
+ session.peerConnection.close();
106
+ }
107
+
108
+ private async _createRtcConnection(): Promise<RTCPeerConnection> {
109
+ const peerConnection = new RTCPeerConnection({
110
+ iceServers: this._config.iceServers,
111
+ bundlePolicy: 'max-bundle',
112
+ });
113
+ peerConnection.addEventListener('connectionstatechange', () => {
114
+ if (peerConnection.connectionState === 'failed' || peerConnection.connectionState === 'closed') {
115
+ log.warn('calls connection failed', { state: peerConnection.connectionState });
116
+ void this._persistentLifecycle.scheduleRestart();
117
+ }
118
+ });
119
+
120
+ let iceTimeout = -1;
121
+ peerConnection.addEventListener('iceconnectionstatechange', () => {
122
+ clearTimeout(iceTimeout);
123
+ if (peerConnection.iceConnectionState === 'failed' || peerConnection.iceConnectionState === 'closed') {
124
+ log.warn('calls connection to ICEs failed', { state: peerConnection.iceConnectionState });
125
+ void this._persistentLifecycle.scheduleRestart();
126
+ } else if (peerConnection.iceConnectionState === 'disconnected') {
127
+ // TODO(mykola): we should start to inspect the connection stats from here on for
128
+ // any other signs of trouble to guide what to do next (instead of just hoping
129
+ // for the best like we do here for now)
130
+ const timeoutSeconds = 7;
131
+ iceTimeout = window.setTimeout(() => {
132
+ if (peerConnection.iceConnectionState === 'connected') {
133
+ log.info('calls iceConnectionState is connected');
134
+ return;
135
+ }
136
+
137
+ log.warn('calls iceConnectionState timed out', { state: peerConnection.iceConnectionState, timeoutSeconds });
138
+ void this._persistentLifecycle.scheduleRestart();
139
+ }, timeoutSeconds * 1_000);
140
+ }
141
+ });
142
+
143
+ return peerConnection;
144
+ }
145
+
146
+ private async _initiateCallSession(): Promise<string> {
147
+ log('creating new session');
148
+ const { sessionId } = await this._fetch<SessionResponse>('/sessions/new?SESSION', { method: 'POST' });
149
+ return sessionId;
150
+ }
151
+
152
+ private async _fetch<T extends ErrorResponse>(relativePath: string, requestInit?: RequestInit): Promise<T> {
153
+ this.history.push({
154
+ type: 'request',
155
+ time: new Date().toISOString(),
156
+ method: requestInit?.method ?? 'GET',
157
+ body: requestInit?.body,
158
+ });
159
+ // TODO(mykola): Handle access control. Use EdgeClient.
160
+ const response = await fetch(`${this._config.apiBase}${relativePath}`, { ...requestInit, redirect: 'manual' });
161
+ if (response.status >= 400) {
162
+ log.error('error while fetching from Calls service', { status: response.status, text: await response.text() });
163
+ throw new Error('Error while fetching from Calls service');
164
+ }
165
+
166
+ try {
167
+ const data = (await response.clone().json()) as T;
168
+ this.history.push({
169
+ type: 'response',
170
+ time: new Date().toISOString(),
171
+ status: response.status,
172
+ json: data,
173
+ });
174
+ if (data.errorCode) {
175
+ throw new Error(data.errorDescription);
176
+ }
177
+
178
+ return data;
179
+ } catch (error) {
180
+ const text = await response.text();
181
+ this.history.push({
182
+ type: 'response',
183
+ time: new Date().toISOString(),
184
+ status: response.status,
185
+ text,
186
+ });
187
+ log.error('Error parsing response from Calls service', {
188
+ error,
189
+ text,
190
+ status: response.status,
191
+ });
192
+ throw error;
193
+ }
194
+ }
195
+
196
+ private async _pushTrackInBulk({
197
+ ctx,
198
+ peerConnection,
199
+ transceiver,
200
+ sessionId,
201
+ trackName,
202
+ }: {
203
+ peerConnection: RTCPeerConnection;
204
+ transceiver: RTCRtpTransceiver;
205
+ sessionId: string;
206
+ trackName: string;
207
+ ctx?: Context;
208
+ }): Promise<TrackObject | void> {
209
+ log.verbose('pushing track ', trackName);
210
+ const pushedTrackPromise = this.pushTrackDispatcher
211
+ .doBulkRequest({ trackName, transceiver }, (tracks) =>
212
+ this.taskScheduler.schedule(async () => {
213
+ // Create offer.
214
+ // Turn on Opus DTX to save bandwidth and set the offer as the local description.
215
+ const offer = await peerConnection.createOffer();
216
+ offer.sdp = offer.sdp?.replace('useinbandfec=1', 'usedtx=1;useinbandfec=1');
217
+ await peerConnection.setLocalDescription(offer);
218
+
219
+ const response = await this._fetch<TracksResponse>(`/sessions/${sessionId}/tracks/new?PUSHING`, {
220
+ method: 'POST',
221
+ body: JSON.stringify({
222
+ sessionDescription: {
223
+ sdp: offer.sdp,
224
+ type: 'offer',
225
+ },
226
+ tracks: tracks.map(({ trackName, transceiver }) => ({
227
+ trackName,
228
+ mid: transceiver.mid,
229
+ location: 'local',
230
+ })),
231
+ }),
232
+ });
233
+
234
+ invariant(response.tracks !== undefined);
235
+ if (!response.errorCode) {
236
+ await peerConnection.setRemoteDescription(new RTCSessionDescription(response.sessionDescription));
237
+ await peerConnectionIsConnected(peerConnection);
238
+ }
239
+
240
+ return {
241
+ tracks: response.tracks,
242
+ };
243
+ }),
244
+ )
245
+ .then(({ tracks }) => {
246
+ const trackData = tracks.find((t) => t.mid === transceiver.mid);
247
+ if (trackData) {
248
+ return {
249
+ ...trackData,
250
+ sessionId,
251
+ location: 'remote',
252
+ } satisfies TrackObject;
253
+ } else {
254
+ log.error('missing track data');
255
+ }
256
+ })
257
+ .catch((err) => {
258
+ log.catch(err);
259
+ });
260
+
261
+ const onDispose = async () => {
262
+ if (transceiver.mid) {
263
+ await this._closeTrackInBulk(peerConnection, transceiver.mid, sessionId);
264
+ }
265
+ };
266
+
267
+ ctx?.onDispose(async () => await onDispose());
268
+ this._ctx.onDispose(() => onDispose());
269
+
270
+ return pushedTrackPromise;
271
+ }
272
+
273
+ async pushTrack({
274
+ track,
275
+ encodings = [],
276
+ ctx,
277
+ previousTrack,
278
+ }: {
279
+ track: MediaStreamTrack | null;
280
+ encodings?: RTCRtpEncodingParameters[];
281
+ ctx?: Context;
282
+ previousTrack?: TrackObject;
283
+ }): Promise<TrackObject | undefined> {
284
+ await this.waitUntilOpen();
285
+ invariant(this.session);
286
+ log('trying to push track', { track, previousTrack, encodings });
287
+ if (previousTrack) {
288
+ const transceiver = this.session.peerConnection.getTransceivers().find((t) => t.mid === previousTrack.mid);
289
+ if (transceiver?.sender.track === track) {
290
+ log.verbose('track already pushed', { track, previousTrack, transceiver });
291
+ return previousTrack;
292
+ } else if (transceiver) {
293
+ log.verbose('replacing track', { track, previousTrack, transceiver });
294
+ transceiver.sender.replaceTrack(track).catch((err) => log.catch(err));
295
+ return previousTrack;
296
+ }
297
+ }
298
+ invariant(track);
299
+
300
+ // We want a single id for this connection.
301
+ const stableId = crypto.randomUUID();
302
+ const transceiver = this.session.peerConnection!.addTransceiver(track, { direction: 'sendonly' });
303
+ log.verbose('creating transceiver');
304
+
305
+ const pushedTrackData = await this._pushTrackInBulk({
306
+ ctx,
307
+ peerConnection: this.session.peerConnection!,
308
+ transceiver,
309
+ sessionId: this.session.sessionId!,
310
+ trackName: stableId,
311
+ });
312
+ invariant(pushedTrackData);
313
+
314
+ const parameters = transceiver.sender.getParameters();
315
+ encodings.forEach((encoding, i) => {
316
+ const existing = parameters.encodings[i];
317
+ parameters.encodings[i] = { ...existing, ...encoding };
318
+ });
319
+ transceiver.sender.setParameters(parameters).catch((err) => log.catch(err));
320
+
321
+ return pushedTrackData;
322
+ }
323
+
324
+ // TODO(mykola): Add retry logic if the tracks fails to pull.
325
+ private async _pullTrackInBulk({
326
+ peerConnection,
327
+ sessionId,
328
+ trackData,
329
+ ctx,
330
+ }: {
331
+ peerConnection: RTCPeerConnection;
332
+ sessionId: string;
333
+ trackData: TrackObject;
334
+ ctx?: Context;
335
+ }): Promise<MediaStreamTrack | undefined> {
336
+ let mid = '';
337
+ log('pulling track', { trackName: trackData.trackName });
338
+ const pulledTrackPromise = this.pullTrackDispatcher
339
+ .doBulkRequest(trackData, (tracks) =>
340
+ this.taskScheduler.schedule(async () => {
341
+ const newTrackResponse = await this._fetch<TracksResponse>(`/sessions/${sessionId}/tracks/new?PULLING`, {
342
+ method: 'POST',
343
+ body: JSON.stringify({ tracks }),
344
+ });
345
+
346
+ if (newTrackResponse.errorCode) {
347
+ throw new Error(newTrackResponse.errorDescription);
348
+ }
349
+
350
+ invariant(newTrackResponse.tracks);
351
+ const trackMap = tracks.reduce((acc, track) => {
352
+ const pulledTrackData = newTrackResponse.tracks?.find(
353
+ (t) => t.trackName === track.trackName && t.sessionId === track.sessionId,
354
+ );
355
+
356
+ if (pulledTrackData && pulledTrackData.mid) {
357
+ acc.set(track, {
358
+ mid: pulledTrackData.mid,
359
+ resolvedTrack: resolveTrack(peerConnection, (t) => t.mid === pulledTrackData.mid),
360
+ });
361
+ }
362
+
363
+ return acc;
364
+ }, new Map<TrackObject, { resolvedTrack: Promise<MediaStreamTrack>; mid: string }>());
365
+
366
+ if (newTrackResponse.requiresImmediateRenegotiation) {
367
+ await peerConnection.setRemoteDescription(new RTCSessionDescription(newTrackResponse.sessionDescription));
368
+ const answer = await peerConnection.createAnswer();
369
+ await peerConnection.setLocalDescription(answer);
370
+
371
+ const renegotiationResponse = await this._fetch<RenegotiationResponse>(
372
+ `/sessions/${sessionId}/renegotiate`,
373
+ {
374
+ method: 'PUT',
375
+ body: JSON.stringify({
376
+ sessionDescription: {
377
+ type: 'answer',
378
+ sdp: peerConnection.currentLocalDescription?.sdp,
379
+ },
380
+ }),
381
+ },
382
+ );
383
+
384
+ if (renegotiationResponse.errorCode) {
385
+ throw new Error(renegotiationResponse.errorDescription);
386
+ } else {
387
+ await peerConnectionIsConnected(peerConnection);
388
+ }
389
+ }
390
+
391
+ return { trackMap };
392
+ }),
393
+ )
394
+ .then(({ trackMap }) => {
395
+ const trackInfo = trackMap.get(trackData);
396
+ if (trackInfo) {
397
+ return trackInfo.resolvedTrack
398
+ .then((track) => {
399
+ mid = trackInfo.mid;
400
+ return track;
401
+ })
402
+ .catch((err) => log.catch(err));
403
+ } else {
404
+ log.error('missing track info');
405
+ }
406
+ });
407
+
408
+ const onDispose = async () => {
409
+ if (mid) {
410
+ log('closing pulled track ', trackData.trackName);
411
+ await this._closeTrackInBulk(peerConnection, mid, sessionId);
412
+ mid = '';
413
+ }
414
+ };
415
+
416
+ this._ctx.onDispose(() => onDispose());
417
+ ctx?.onDispose(() => onDispose());
418
+
419
+ return pulledTrackPromise as Promise<MediaStreamTrack | undefined>;
420
+ }
421
+
422
+ async pullTrack({
423
+ ctx,
424
+ trackData,
425
+ }: {
426
+ trackData: TrackObject;
427
+ ctx?: Context;
428
+ }): Promise<MediaStreamTrack | undefined> {
429
+ await this.waitUntilOpen();
430
+
431
+ invariant(this.session);
432
+ const session = this.session;
433
+ return this._pullTrackInBulk({
434
+ peerConnection: session.peerConnection,
435
+ sessionId: session.sessionId,
436
+ trackData,
437
+ ctx,
438
+ });
439
+ }
440
+
441
+ async _closeTrackInBulk(peerConnection: RTCPeerConnection, mid: string, sessionId: string): Promise<void> {
442
+ const transceiver = peerConnection.getTransceivers().find((t) => t.mid === mid);
443
+
444
+ if (peerConnection.connectionState !== 'connected' || transceiver === undefined) {
445
+ return;
446
+ }
447
+ await this.closeTrackDispatcher.doBulkRequest({ mid }, (mids) =>
448
+ this.taskScheduler.schedule(async () => {
449
+ transceiver.stop();
450
+ const activeTransceivers = peerConnection
451
+ .getTransceivers()
452
+ .filter((t) => t.mid !== mid && t.direction !== 'stopped');
453
+
454
+ let requestBody;
455
+ if (activeTransceivers.length > 0) {
456
+ // create an offer
457
+ const offer = await peerConnection.createOffer();
458
+ // And set the offer as the local description
459
+ await peerConnection.setLocalDescription(offer);
460
+ requestBody = {
461
+ tracks: mids,
462
+ sessionDescription: {
463
+ sdp: peerConnection.localDescription?.sdp,
464
+ type: 'offer',
465
+ },
466
+ force: false,
467
+ };
468
+ } else {
469
+ requestBody = {
470
+ tracks: mids,
471
+ force: true,
472
+ };
473
+ }
474
+
475
+ const response = await this._fetch<TracksResponse>(`/sessions/${sessionId}/tracks/close`, {
476
+ method: 'PUT',
477
+ body: JSON.stringify(requestBody),
478
+ });
479
+
480
+ if (requestBody.sessionDescription) {
481
+ await peerConnection.setRemoteDescription(new RTCSessionDescription(response.sessionDescription));
482
+ }
483
+ }),
484
+ );
485
+ }
486
+ }
487
+
488
+ const resolveTrack = async (
489
+ peerConnection: RTCPeerConnection,
490
+ compare: (t: RTCRtpTransceiver) => boolean,
491
+ timeout = NETWORK_TIMEOUT,
492
+ ) =>
493
+ new Promise<MediaStreamTrack>((resolve, reject) => {
494
+ setTimeout(reject, timeout);
495
+ const handler = () => {
496
+ const transceiver = peerConnection.getTransceivers().find(compare);
497
+ if (transceiver) {
498
+ resolve(transceiver.receiver.track);
499
+ peerConnection.removeEventListener('track', handler);
500
+ }
501
+ };
502
+
503
+ peerConnection.addEventListener('track', handler);
504
+ });
505
+
506
+ const peerConnectionIsConnected = async (peerConnection: RTCPeerConnection) => {
507
+ if (peerConnection.connectionState !== 'connected') {
508
+ const connected = new Promise((resolve, reject) => {
509
+ // Timeout after 5s.
510
+ const timeout = setTimeout(() => {
511
+ peerConnection.removeEventListener('connectionstatechange', connectionStateChangeHandler);
512
+ reject(new Error('Connection timeout'));
513
+ }, NETWORK_TIMEOUT);
514
+
515
+ const connectionStateChangeHandler = () => {
516
+ if (peerConnection.connectionState === 'connected') {
517
+ peerConnection.removeEventListener('connectionstatechange', connectionStateChangeHandler);
518
+ clearTimeout(timeout);
519
+ resolve(undefined);
520
+ }
521
+ };
522
+ peerConnection.addEventListener('connectionstatechange', connectionStateChangeHandler);
523
+ });
524
+
525
+ await connected;
526
+ }
527
+ };
@@ -0,0 +1,27 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ /**
6
+ * Array that holds the last N items.
7
+ */
8
+ export class HistoryCache<T> {
9
+ private readonly _limit: number;
10
+ private readonly _messages: T[];
11
+
12
+ constructor(limit = 100) {
13
+ this._limit = limit;
14
+ this._messages = [];
15
+ }
16
+
17
+ push(...messages: T[]): void {
18
+ if (this._messages.length >= this._limit) {
19
+ this._messages.shift(); // Remove the oldest message
20
+ }
21
+ this._messages.push(...messages); // Add new message
22
+ }
23
+
24
+ get(): T[] {
25
+ return this._messages;
26
+ }
27
+ }
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './audio';
6
+ export * from './calls-service';
7
+ export * from './media';
8
+ export * from './stub-media-streams';
@@ -0,0 +1,52 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { log } from '@dxos/log';
6
+
7
+ export const getUserMediaTrack = async (
8
+ kind: MediaDeviceKind,
9
+ constraints: MediaTrackConstraints = {},
10
+ deviceList?: MediaDeviceInfo[],
11
+ ): Promise<MediaStreamTrack> => {
12
+ deviceList ??= (await getDeviceList()).filter((device) => device.kind === kind);
13
+ const firstDevice = deviceList[0];
14
+ if (!firstDevice) {
15
+ throw new Error('No devices found');
16
+ }
17
+
18
+ return acquireTrack(firstDevice, constraints);
19
+ };
20
+
21
+ const acquireTrack = async (device: MediaDeviceInfo, constraints: MediaTrackConstraints): Promise<MediaStreamTrack> => {
22
+ const { deviceId, label } = device;
23
+ log.info(`requesting ${label}`);
24
+
25
+ const mediaStream = await navigator.mediaDevices.getUserMedia(
26
+ device.kind === 'videoinput' ? { video: { ...constraints, deviceId } } : { audio: { ...constraints, deviceId } },
27
+ );
28
+ return device.kind === 'videoinput' ? mediaStream.getVideoTracks()[0] : mediaStream.getAudioTracks()[0];
29
+ };
30
+
31
+ // TODO(mykola): Handle devicechange event.
32
+ export const getDeviceList = () => navigator.mediaDevices.enumerateDevices();
33
+
34
+ const SCREENSHARE_CONSTRAINTS = {
35
+ video: {
36
+ width: { max: 1920 },
37
+ height: { max: 1080 },
38
+ frameRate: { max: 10 },
39
+ },
40
+ };
41
+
42
+ export const getScreenshare = async ({ contentHint }: { contentHint: string }) => {
43
+ // Get 1080p equivalent screenshare with 10fps, but save natural sharing content aspect ratio.
44
+ const ms = await navigator.mediaDevices.getDisplayMedia(SCREENSHARE_CONSTRAINTS);
45
+ ms.getVideoTracks().forEach((track) => {
46
+ if (contentHint && 'contentHint' in track) {
47
+ track.contentHint = contentHint;
48
+ }
49
+ });
50
+
51
+ return ms;
52
+ };
@@ -0,0 +1,73 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+ //
5
+
6
+ import { waitForCondition, scheduleTaskInterval } from '@dxos/async';
7
+ import { cancelWithContext, type Context } from '@dxos/context';
8
+ import { invariant } from '@dxos/invariant';
9
+
10
+ /**
11
+ * Creates a black canvas stream track.
12
+ * We need this to to have black rectangle video that is live (have frames) when video feed is disabled.
13
+ */
14
+ export const createBlackCanvasStreamTrack = async ({
15
+ ctx,
16
+ width,
17
+ height,
18
+ }: {
19
+ ctx: Context;
20
+ width: number;
21
+ height: number;
22
+ }): Promise<MediaStreamTrack> => {
23
+ const canvas = document.createElement('canvas');
24
+ canvas.width = width;
25
+ canvas.height = height;
26
+
27
+ const canvasCtx = canvas.getContext('2d');
28
+ invariant(canvasCtx);
29
+ const drawFrame = () => {
30
+ canvasCtx.fillStyle = 'black';
31
+ canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
32
+ };
33
+ scheduleTaskInterval(ctx, async () => drawFrame(), 1_000);
34
+
35
+ const track = canvas.captureStream().getVideoTracks()[0];
36
+ drawFrame();
37
+
38
+ await cancelWithContext(ctx, waitForCondition({ condition: () => track.readyState === 'live', timeout: 1_000 }));
39
+
40
+ return track;
41
+ };
42
+
43
+ /**
44
+ * Creates an inaudible audio stream track.
45
+ * We need this to have audio track available when audio is disabled.
46
+ */
47
+ export const createInaudibleAudioStreamTrack = async ({ ctx }: { ctx: Context }): Promise<MediaStreamTrack> => {
48
+ const audioContext = new window.AudioContext();
49
+ const oscillator = audioContext.createOscillator();
50
+ oscillator.type = 'triangle';
51
+
52
+ // set to 0.1 to make inaudible
53
+ oscillator.frequency.setValueAtTime(0.1, audioContext.currentTime);
54
+
55
+ const gainNode = audioContext.createGain();
56
+ // even w/ gain at 0 some packets are sent
57
+ gainNode.gain.setValueAtTime(0, audioContext.currentTime);
58
+
59
+ oscillator.connect(gainNode);
60
+
61
+ const destination = audioContext.createMediaStreamDestination();
62
+ gainNode.connect(destination);
63
+
64
+ oscillator.start();
65
+
66
+ ctx.onDispose(async () => {
67
+ oscillator.disconnect();
68
+ oscillator.stop();
69
+ await audioContext.close();
70
+ });
71
+
72
+ return destination.stream.getAudioTracks()[0];
73
+ };