@dxos/plugin-thread 0.8.4-main.c1de068 → 0.8.4-main.c4373fc
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.
- package/dist/lib/browser/ChannelContainer-BGDGZHV4.mjs +16 -0
- package/dist/lib/browser/ChatContainer-75KJZ4XR.mjs +15 -0
- package/dist/lib/browser/{app-graph-builder-N7BE5TYW.mjs → app-graph-builder-ODKIAXR7.mjs} +19 -19
- package/dist/lib/browser/app-graph-builder-ODKIAXR7.mjs.map +7 -0
- package/dist/lib/browser/{call-manager-NIPDYUVD.mjs → call-manager-BCKLI4QD.mjs} +7 -7
- package/dist/lib/browser/call-manager-BCKLI4QD.mjs.map +7 -0
- package/dist/lib/browser/chunk-2MUYQOQE.mjs +23 -0
- package/dist/lib/browser/{chunk-4OYAYON2.mjs.map → chunk-2MUYQOQE.mjs.map} +3 -3
- package/dist/lib/browser/chunk-B7YQ7QR2.mjs +376 -0
- package/dist/lib/browser/chunk-B7YQ7QR2.mjs.map +7 -0
- package/dist/lib/browser/{chunk-KWPRNHTK.mjs → chunk-CMCVHULC.mjs} +235 -233
- package/dist/lib/browser/chunk-CMCVHULC.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GERVMFQW.mjs → chunk-CP7E2HRE.mjs} +2 -2
- package/dist/lib/browser/chunk-CP7E2HRE.mjs.map +7 -0
- package/dist/lib/browser/{chunk-ERWASUQK.mjs → chunk-HDEQRBOC.mjs} +38 -6
- package/dist/lib/{node-esm/chunk-25ABSYP2.mjs.map → browser/chunk-HDEQRBOC.mjs.map} +4 -4
- package/dist/lib/browser/chunk-L326LSNS.mjs +18 -0
- package/dist/lib/browser/chunk-L326LSNS.mjs.map +7 -0
- package/dist/lib/browser/{chunk-EV4JWI7L.mjs → chunk-T4KJ5JR4.mjs} +5 -7
- package/dist/lib/browser/chunk-T4KJ5JR4.mjs.map +7 -0
- package/dist/lib/browser/chunk-VJMKS4CZ.mjs +619 -0
- package/dist/lib/browser/chunk-VJMKS4CZ.mjs.map +7 -0
- package/dist/lib/browser/{chunk-GCLD5ESP.mjs → chunk-VTYX3SIM.mjs} +26 -16
- package/dist/lib/browser/chunk-VTYX3SIM.mjs.map +7 -0
- package/dist/lib/browser/{chunk-6NSMBO3B.mjs → chunk-XYUJXHYV.mjs} +55 -5
- package/dist/lib/browser/chunk-XYUJXHYV.mjs.map +7 -0
- package/dist/lib/browser/{chunk-ZE7KWN36.mjs → chunk-ZCEU5K23.mjs} +461 -240
- package/dist/lib/browser/chunk-ZCEU5K23.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +23 -14
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-JFGLZ6VI.mjs → intent-resolver-VOQD3TQ2.mjs} +75 -17
- package/dist/lib/browser/intent-resolver-VOQD3TQ2.mjs.map +7 -0
- package/dist/lib/browser/{markdown-GEXBVJYN.mjs → markdown-R6MGRIA6.mjs} +7 -7
- package/dist/lib/browser/markdown-R6MGRIA6.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/react-root-UMSBTN6H.mjs +28 -0
- package/dist/lib/browser/react-root-UMSBTN6H.mjs.map +7 -0
- package/dist/lib/browser/{react-surface-YCYEFBPM.mjs → react-surface-SXMQEWKE.mjs} +22 -22
- package/dist/lib/browser/react-surface-SXMQEWKE.mjs.map +7 -0
- package/dist/lib/browser/{settings-E32MTL7A.mjs → settings-TCNF54PN.mjs} +5 -5
- package/dist/lib/browser/{settings-E32MTL7A.mjs.map → settings-TCNF54PN.mjs.map} +1 -1
- package/dist/lib/browser/{state-H6CYONWE.mjs → state-ZQ4IPOJT.mjs} +5 -5
- package/dist/lib/browser/state-ZQ4IPOJT.mjs.map +7 -0
- package/dist/lib/browser/toolkit-JF4B4QIO.mjs +84 -0
- package/dist/lib/browser/toolkit-JF4B4QIO.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +2 -2
- package/dist/lib/node-esm/ChannelContainer-OXC6BTU7.mjs +17 -0
- package/dist/lib/node-esm/{ChatContainer-IQUWU426.mjs → ChatContainer-SH6LTFQL.mjs} +6 -6
- package/dist/lib/node-esm/{app-graph-builder-VCW76Z6V.mjs → app-graph-builder-DMPT3MY6.mjs} +19 -19
- package/dist/lib/node-esm/app-graph-builder-DMPT3MY6.mjs.map +7 -0
- package/dist/lib/node-esm/{call-manager-D6O5ZJJ4.mjs → call-manager-Q7RY2YP4.mjs} +7 -7
- package/dist/lib/node-esm/call-manager-Q7RY2YP4.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-C3XFMUAY.mjs → chunk-2KZ4ODHU.mjs} +235 -233
- package/dist/lib/node-esm/chunk-2KZ4ODHU.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-25ABSYP2.mjs → chunk-2MMJF7DW.mjs} +38 -6
- package/dist/lib/node-esm/chunk-2MMJF7DW.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-OU5BYIDO.mjs → chunk-2V3WSLUZ.mjs} +55 -5
- package/dist/lib/node-esm/chunk-2V3WSLUZ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-57XR3LEF.mjs → chunk-ABUFOZVX.mjs} +2 -2
- package/dist/lib/node-esm/chunk-ABUFOZVX.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-TDFHTCZZ.mjs → chunk-BAMZXJS4.mjs} +26 -16
- package/dist/lib/node-esm/chunk-BAMZXJS4.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-BTGKI5IK.mjs +377 -0
- package/dist/lib/node-esm/chunk-BTGKI5IK.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-GZ4WL2IS.mjs +25 -0
- package/dist/lib/node-esm/{chunk-GW37AAM4.mjs.map → chunk-GZ4WL2IS.mjs.map} +3 -3
- package/dist/lib/node-esm/chunk-LCHTNRK7.mjs +19 -0
- package/dist/lib/node-esm/chunk-LCHTNRK7.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-NHSIAEDW.mjs +620 -0
- package/dist/lib/node-esm/chunk-NHSIAEDW.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-GRLRWTRU.mjs → chunk-OBVAURUU.mjs} +5 -7
- package/dist/lib/node-esm/chunk-OBVAURUU.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-IHDNQLLR.mjs → chunk-TIPIOH2J.mjs} +461 -240
- package/dist/lib/node-esm/chunk-TIPIOH2J.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +23 -14
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/{intent-resolver-OXGNQ44Y.mjs → intent-resolver-AKVH66PH.mjs} +75 -17
- package/dist/lib/node-esm/intent-resolver-AKVH66PH.mjs.map +7 -0
- package/dist/lib/node-esm/{markdown-LIJZDK2J.mjs → markdown-MYGTDXT6.mjs} +7 -7
- package/dist/lib/node-esm/markdown-MYGTDXT6.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/react-root-W6AVTL5M.mjs +29 -0
- package/dist/lib/node-esm/react-root-W6AVTL5M.mjs.map +7 -0
- package/dist/lib/node-esm/{react-surface-IRSEKM42.mjs → react-surface-NLSZJGMO.mjs} +22 -22
- package/dist/lib/node-esm/react-surface-NLSZJGMO.mjs.map +7 -0
- package/dist/lib/node-esm/{settings-SJ24YG7M.mjs → settings-ZQHGVFBY.mjs} +5 -5
- package/dist/lib/node-esm/{settings-SJ24YG7M.mjs.map → settings-ZQHGVFBY.mjs.map} +1 -1
- package/dist/lib/node-esm/{state-D7WNDT3N.mjs → state-FSLLJ5TZ.mjs} +5 -5
- package/dist/lib/node-esm/state-FSLLJ5TZ.mjs.map +7 -0
- package/dist/lib/node-esm/toolkit-DHPKJITO.mjs +85 -0
- package/dist/lib/node-esm/toolkit-DHPKJITO.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +2 -2
- package/dist/types/src/ThreadPlugin.d.ts +1 -1
- package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
- package/dist/types/src/calls/call-manager.d.ts.map +1 -1
- package/dist/types/src/calls/call-swarm-synchronizer.d.ts.map +1 -1
- package/dist/types/src/calls/media-manager.d.ts +1 -1
- package/dist/types/src/calls/media-manager.d.ts.map +1 -1
- package/dist/types/src/calls/types.d.ts +1 -1
- package/dist/types/src/calls/types.d.ts.map +1 -1
- package/dist/types/src/calls/util/calls-service.d.ts +4 -2
- package/dist/types/src/calls/util/calls-service.d.ts.map +1 -1
- package/dist/types/src/calls/util/stub-media-streams.d.ts.map +1 -1
- package/dist/types/src/calls-service.stories.d.ts +10 -9
- package/dist/types/src/calls-service.stories.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/call-manager.d.ts +1 -1
- package/dist/types/src/capabilities/call-manager.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +2 -2
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +11 -10
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/markdown.d.ts +1 -1
- package/dist/types/src/capabilities/markdown.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-root.d.ts +2 -2
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +1 -1
- package/dist/types/src/capabilities/state.d.ts +2 -2
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/capabilities/toolkit.d.ts +6 -0
- package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
- package/dist/types/src/components/Call/Call.d.ts.map +1 -1
- package/dist/types/src/components/Call/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/CallDebugPanel.d.ts +7 -0
- package/dist/types/src/components/CallDebugPanel.d.ts.map +1 -1
- package/dist/types/src/components/CallSidebar.d.ts.map +1 -1
- package/dist/types/src/components/ChannelContainer.d.ts.map +1 -1
- package/dist/types/src/components/ChannelContainer.stories.d.ts +89 -5
- package/dist/types/src/components/ChannelContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/Chat.stories.d.ts +91 -5
- package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
- package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
- package/dist/types/src/components/Comments.stories.d.ts +160 -4
- package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
- package/dist/types/src/components/CommentsThreadContainer.d.ts +2 -2
- package/dist/types/src/components/CommentsThreadContainer.d.ts.map +1 -1
- package/dist/types/src/components/Lobby/Lobby.d.ts.map +1 -1
- package/dist/types/src/components/Media/VideoObject.d.ts +2 -2
- package/dist/types/src/components/Media/VideoObject.d.ts.map +1 -1
- package/dist/types/src/components/Media/VideoObject.stories.d.ts +18 -4
- package/dist/types/src/components/Media/VideoObject.stories.d.ts.map +1 -1
- package/dist/types/src/components/MessageContainer.d.ts +4 -1
- package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
- package/dist/types/src/components/Participant/Participant.d.ts.map +1 -1
- package/dist/types/src/components/Participant/ParticipantGrid.d.ts.map +1 -1
- package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts +155 -3
- package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts +157 -4
- package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts.map +1 -1
- package/dist/types/src/components/ResponsiveGrid/ResponsiveGridItem.d.ts.map +1 -1
- package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
- package/dist/types/src/components/ThreadSettings.d.ts +1 -1
- package/dist/types/src/components/ThreadSettings.d.ts.map +1 -1
- package/dist/types/src/components/command-extension.d.ts.map +1 -1
- package/dist/types/src/components/testing.d.ts +1 -0
- package/dist/types/src/components/testing.d.ts.map +1 -1
- package/dist/types/src/extensions/threads.d.ts +2 -2
- package/dist/types/src/extensions/threads.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useBlackCanvasStreamTrack.d.ts +2 -0
- package/dist/types/src/hooks/useBlackCanvasStreamTrack.d.ts.map +1 -0
- package/dist/types/src/hooks/useVideoStreamTrack.d.ts +1 -1
- package/dist/types/src/hooks/useVideoStreamTrack.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +1 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +126 -109
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +259 -172
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +62 -58
- package/src/ThreadPlugin.tsx +130 -124
- package/src/calls/call-manager.ts +2 -2
- package/src/calls/call-swarm-synchronizer.ts +1 -1
- package/src/calls/media-manager.ts +60 -34
- package/src/calls/types.ts +8 -4
- package/src/calls/util/calls-service.ts +65 -14
- package/src/calls/util/stub-media-streams.ts +62 -5
- package/src/calls-service.stories.tsx +78 -65
- package/src/capabilities/app-graph-builder.ts +18 -14
- package/src/capabilities/call-manager.ts +3 -2
- package/src/capabilities/capabilities.ts +7 -7
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/intent-resolver.ts +43 -9
- package/src/capabilities/markdown.ts +4 -3
- package/src/capabilities/react-root.tsx +2 -2
- package/src/capabilities/react-surface.tsx +14 -13
- package/src/capabilities/state.ts +2 -1
- package/src/capabilities/toolkit.ts +82 -0
- package/src/components/Call/Call.tsx +3 -2
- package/src/components/Call/Toolbar.tsx +5 -5
- package/src/components/CallDebugPanel.tsx +108 -49
- package/src/components/CallSidebar.tsx +2 -1
- package/src/components/ChannelContainer.stories.tsx +11 -10
- package/src/components/ChannelContainer.tsx +8 -7
- package/src/components/Chat.stories.tsx +34 -31
- package/src/components/ChatContainer.tsx +8 -4
- package/src/components/Comments.stories.tsx +39 -38
- package/src/components/CommentsContainer.tsx +2 -1
- package/src/components/CommentsThreadContainer.tsx +9 -7
- package/src/components/Lobby/Lobby.tsx +1 -1
- package/src/components/Media/VideoObject.stories.tsx +36 -28
- package/src/components/MessageContainer.tsx +43 -9
- package/src/components/Participant/Participant.tsx +13 -7
- package/src/components/Participant/ParticipantGrid.tsx +2 -1
- package/src/components/ResponsiveGrid/ResponsiveContainer.stories.tsx +8 -8
- package/src/components/ResponsiveGrid/ResponsiveGrid.stories.tsx +44 -42
- package/src/components/ResponsiveGrid/ResponsiveGrid.tsx +1 -1
- package/src/components/ResponsiveGrid/ResponsiveGridItem.tsx +2 -2
- package/src/components/ThreadComplementary.tsx +37 -7
- package/src/components/ThreadSettings.tsx +6 -7
- package/src/components/command-extension.ts +5 -2
- package/src/components/testing.ts +22 -0
- package/src/extensions/threads.ts +6 -6
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useBlackCanvasStreamTrack.tsx +27 -0
- package/src/hooks/useDebugMode.ts +1 -1
- package/src/hooks/useVideoStreamTrack.tsx +6 -3
- package/src/meta.ts +3 -4
- package/src/testing/stories.ts +3 -3
- package/src/translations.ts +3 -1
- package/src/types/schema.ts +1 -1
- package/src/types/types.ts +22 -13
- package/src/typings/peermetrics-webrtc-stats.d.ts +17 -1
- package/src/typings.d.ts +8 -0
- package/src/util.ts +1 -1
- package/dist/lib/browser/ChannelContainer-IY2UIRLF.mjs +0 -16
- package/dist/lib/browser/ChatContainer-AIGIWAUG.mjs +0 -15
- package/dist/lib/browser/app-graph-builder-N7BE5TYW.mjs.map +0 -7
- package/dist/lib/browser/call-manager-NIPDYUVD.mjs.map +0 -7
- package/dist/lib/browser/chunk-4OYAYON2.mjs +0 -21
- package/dist/lib/browser/chunk-6NSMBO3B.mjs.map +0 -7
- package/dist/lib/browser/chunk-ERWASUQK.mjs.map +0 -7
- package/dist/lib/browser/chunk-EV4JWI7L.mjs.map +0 -7
- package/dist/lib/browser/chunk-GCLD5ESP.mjs.map +0 -7
- package/dist/lib/browser/chunk-GERVMFQW.mjs.map +0 -7
- package/dist/lib/browser/chunk-KWPRNHTK.mjs.map +0 -7
- package/dist/lib/browser/chunk-LGSDKXPF.mjs +0 -18
- package/dist/lib/browser/chunk-LGSDKXPF.mjs.map +0 -7
- package/dist/lib/browser/chunk-VUWC4IJM.mjs +0 -479
- package/dist/lib/browser/chunk-VUWC4IJM.mjs.map +0 -7
- package/dist/lib/browser/chunk-XLPJXUKO.mjs +0 -367
- package/dist/lib/browser/chunk-XLPJXUKO.mjs.map +0 -7
- package/dist/lib/browser/chunk-ZE7KWN36.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-JFGLZ6VI.mjs.map +0 -7
- package/dist/lib/browser/markdown-GEXBVJYN.mjs.map +0 -7
- package/dist/lib/browser/react-root-X4UIVGTX.mjs +0 -28
- package/dist/lib/browser/react-root-X4UIVGTX.mjs.map +0 -7
- package/dist/lib/browser/react-surface-YCYEFBPM.mjs.map +0 -7
- package/dist/lib/browser/state-H6CYONWE.mjs.map +0 -7
- package/dist/lib/node-esm/ChannelContainer-DET6T5N2.mjs +0 -17
- package/dist/lib/node-esm/app-graph-builder-VCW76Z6V.mjs.map +0 -7
- package/dist/lib/node-esm/call-manager-D6O5ZJJ4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2MFNK53T.mjs +0 -480
- package/dist/lib/node-esm/chunk-2MFNK53T.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-57XR3LEF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-75RJ5DJI.mjs +0 -19
- package/dist/lib/node-esm/chunk-75RJ5DJI.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-C3XFMUAY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-GRLRWTRU.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-GW37AAM4.mjs +0 -23
- package/dist/lib/node-esm/chunk-IHDNQLLR.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-J4NSON5Q.mjs +0 -368
- package/dist/lib/node-esm/chunk-J4NSON5Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-OU5BYIDO.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TDFHTCZZ.mjs.map +0 -7
- package/dist/lib/node-esm/intent-resolver-OXGNQ44Y.mjs.map +0 -7
- package/dist/lib/node-esm/markdown-LIJZDK2J.mjs.map +0 -7
- package/dist/lib/node-esm/react-root-M7J6PZVW.mjs +0 -29
- package/dist/lib/node-esm/react-root-M7J6PZVW.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-IRSEKM42.mjs.map +0 -7
- package/dist/lib/node-esm/state-D7WNDT3N.mjs.map +0 -7
- /package/dist/lib/browser/{ChannelContainer-IY2UIRLF.mjs.map → ChannelContainer-BGDGZHV4.mjs.map} +0 -0
- /package/dist/lib/browser/{ChatContainer-AIGIWAUG.mjs.map → ChatContainer-75KJZ4XR.mjs.map} +0 -0
- /package/dist/lib/node-esm/{ChannelContainer-DET6T5N2.mjs.map → ChannelContainer-OXC6BTU7.mjs.map} +0 -0
- /package/dist/lib/node-esm/{ChatContainer-IQUWU426.mjs.map → ChatContainer-SH6LTFQL.mjs.map} +0 -0
package/src/calls/types.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { invariant } from '@dxos/invariant';
|
|
5
6
|
import { buf } from '@dxos/protocols/buf';
|
|
6
7
|
import {
|
|
7
8
|
type ActivitySchema,
|
|
8
|
-
UserStateSchema,
|
|
9
9
|
type UserState as UserStateProto,
|
|
10
|
+
UserStateSchema,
|
|
10
11
|
} from '@dxos/protocols/buf/dxos/edge/calls_pb';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -61,10 +62,13 @@ export type EncodedTrackName = string & { __brand: 'EncodedTrackName' };
|
|
|
61
62
|
|
|
62
63
|
export const TrackNameCodec = {
|
|
63
64
|
encode: (trackData: TrackObject): EncodedTrackName => {
|
|
64
|
-
|
|
65
|
+
invariant(trackData.sessionId);
|
|
66
|
+
invariant(trackData.trackName);
|
|
67
|
+
invariant(trackData.mid);
|
|
68
|
+
return (trackData.sessionId + '/' + trackData.trackName + '/' + trackData.mid) as EncodedTrackName;
|
|
65
69
|
},
|
|
66
70
|
decode: (name: EncodedTrackName): TrackObject => {
|
|
67
|
-
const [sessionId, trackName] = name.split('/');
|
|
68
|
-
return { sessionId, trackName, location: 'remote' };
|
|
71
|
+
const [sessionId, trackName, mid] = name.split('/');
|
|
72
|
+
return { sessionId, trackName, location: 'remote', mid };
|
|
69
73
|
},
|
|
70
74
|
};
|
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { PersistentLifecycle } from '@dxos/async';
|
|
5
|
+
import { PersistentLifecycle, Trigger, scheduleTask } from '@dxos/async';
|
|
6
6
|
import { type Context, Resource } from '@dxos/context';
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { log } from '@dxos/log';
|
|
9
9
|
|
|
10
|
-
import { HistoryCache } from './history-cache';
|
|
11
|
-
import { BulkRequestDispatcher, FIFOScheduler } from './task-scheduling';
|
|
12
10
|
import {
|
|
13
|
-
type SessionResponse,
|
|
14
11
|
type ErrorResponse,
|
|
15
12
|
type RenegotiationResponse,
|
|
13
|
+
type SessionResponse,
|
|
16
14
|
type TrackObject,
|
|
17
15
|
type TracksResponse,
|
|
18
16
|
} from '../types';
|
|
19
17
|
|
|
18
|
+
import { HistoryCache } from './history-cache';
|
|
19
|
+
import { BulkRequestDispatcher, FIFOScheduler } from './task-scheduling';
|
|
20
|
+
|
|
20
21
|
const NETWORK_TIMEOUT = 5_000;
|
|
21
22
|
|
|
22
23
|
export type Session = {
|
|
@@ -32,12 +33,14 @@ export type CallsServiceConfig = {
|
|
|
32
33
|
|
|
33
34
|
export type HistoryRecord =
|
|
34
35
|
| {
|
|
36
|
+
endpoint: string;
|
|
35
37
|
type: 'request';
|
|
36
38
|
time: string;
|
|
37
39
|
method: string;
|
|
38
40
|
body: any;
|
|
39
41
|
}
|
|
40
42
|
| {
|
|
43
|
+
endpoint: string;
|
|
41
44
|
type: 'response';
|
|
42
45
|
time: string;
|
|
43
46
|
status: number;
|
|
@@ -115,7 +118,7 @@ export class CallsServicePeer extends Resource {
|
|
|
115
118
|
});
|
|
116
119
|
peerConnection.addEventListener('connectionstatechange', () => {
|
|
117
120
|
if (peerConnection.connectionState === 'failed' || peerConnection.connectionState === 'closed') {
|
|
118
|
-
log.
|
|
121
|
+
log.info('calls connection failed', { state: peerConnection.connectionState });
|
|
119
122
|
void this._persistentLifecycle.scheduleRestart();
|
|
120
123
|
}
|
|
121
124
|
});
|
|
@@ -124,7 +127,7 @@ export class CallsServicePeer extends Resource {
|
|
|
124
127
|
peerConnection.addEventListener('iceconnectionstatechange', () => {
|
|
125
128
|
clearTimeout(iceTimeout);
|
|
126
129
|
if (peerConnection.iceConnectionState === 'failed' || peerConnection.iceConnectionState === 'closed') {
|
|
127
|
-
log.
|
|
130
|
+
log.info('calls connection to ICEs failed', { state: peerConnection.iceConnectionState });
|
|
128
131
|
void this._persistentLifecycle.scheduleRestart();
|
|
129
132
|
} else if (peerConnection.iceConnectionState === 'disconnected') {
|
|
130
133
|
// TODO(mykola): we should start to inspect the connection stats from here on for
|
|
@@ -137,7 +140,7 @@ export class CallsServicePeer extends Resource {
|
|
|
137
140
|
return;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
|
-
log.
|
|
143
|
+
log.info('calls iceConnectionState timed out', { state: peerConnection.iceConnectionState, timeoutSeconds });
|
|
141
144
|
void this._persistentLifecycle.scheduleRestart();
|
|
142
145
|
}, timeoutSeconds * 1_000);
|
|
143
146
|
}
|
|
@@ -154,6 +157,7 @@ export class CallsServicePeer extends Resource {
|
|
|
154
157
|
|
|
155
158
|
private async _fetch<T extends ErrorResponse>(relativePath: string, requestInit?: RequestInit): Promise<T> {
|
|
156
159
|
this.history.push({
|
|
160
|
+
endpoint: relativePath,
|
|
157
161
|
type: 'request',
|
|
158
162
|
time: new Date().toISOString(),
|
|
159
163
|
method: requestInit?.method ?? 'GET',
|
|
@@ -169,6 +173,7 @@ export class CallsServicePeer extends Resource {
|
|
|
169
173
|
try {
|
|
170
174
|
const data = (await response.clone().json()) as T;
|
|
171
175
|
this.history.push({
|
|
176
|
+
endpoint: relativePath,
|
|
172
177
|
type: 'response',
|
|
173
178
|
time: new Date().toISOString(),
|
|
174
179
|
status: response.status,
|
|
@@ -182,6 +187,7 @@ export class CallsServicePeer extends Resource {
|
|
|
182
187
|
} catch (error) {
|
|
183
188
|
const text = await response.text();
|
|
184
189
|
this.history.push({
|
|
190
|
+
endpoint: relativePath,
|
|
185
191
|
type: 'response',
|
|
186
192
|
time: new Date().toISOString(),
|
|
187
193
|
status: response.status,
|
|
@@ -203,11 +209,11 @@ export class CallsServicePeer extends Resource {
|
|
|
203
209
|
sessionId,
|
|
204
210
|
trackName,
|
|
205
211
|
}: {
|
|
212
|
+
ctx: Context;
|
|
206
213
|
peerConnection: RTCPeerConnection;
|
|
207
214
|
transceiver: RTCRtpTransceiver;
|
|
208
215
|
sessionId: string;
|
|
209
216
|
trackName: string;
|
|
210
|
-
ctx?: Context;
|
|
211
217
|
}): Promise<TrackObject | void> {
|
|
212
218
|
log.verbose('pushing track ', trackName);
|
|
213
219
|
const pushedTrackPromise = this.pushTrackDispatcher
|
|
@@ -245,9 +251,13 @@ export class CallsServicePeer extends Resource {
|
|
|
245
251
|
};
|
|
246
252
|
}),
|
|
247
253
|
)
|
|
248
|
-
.then(({ tracks }) => {
|
|
254
|
+
.then(async ({ tracks }) => {
|
|
249
255
|
const trackData = tracks.find((t) => t.mid === transceiver.mid);
|
|
250
256
|
if (trackData) {
|
|
257
|
+
// we wait for the transceiver to start sending data before we emit
|
|
258
|
+
// the track metadata to ensure that the track will be able to be
|
|
259
|
+
// pulled before making the metadata available to anyone else.
|
|
260
|
+
// await waitForTransceiverToSendData(ctx, transceiver);
|
|
251
261
|
return {
|
|
252
262
|
...trackData,
|
|
253
263
|
sessionId,
|
|
@@ -256,9 +266,6 @@ export class CallsServicePeer extends Resource {
|
|
|
256
266
|
} else {
|
|
257
267
|
log.error('missing track data');
|
|
258
268
|
}
|
|
259
|
-
})
|
|
260
|
-
.catch((err) => {
|
|
261
|
-
log.catch(err);
|
|
262
269
|
});
|
|
263
270
|
|
|
264
271
|
const onDispose = async () => {
|
|
@@ -280,8 +287,8 @@ export class CallsServicePeer extends Resource {
|
|
|
280
287
|
previousTrack,
|
|
281
288
|
}: {
|
|
282
289
|
track: MediaStreamTrack | null;
|
|
290
|
+
ctx: Context;
|
|
283
291
|
encodings?: RTCRtpEncodingParameters[];
|
|
284
|
-
ctx?: Context;
|
|
285
292
|
previousTrack?: TrackObject;
|
|
286
293
|
}): Promise<TrackObject | undefined> {
|
|
287
294
|
await this.waitUntilOpen();
|
|
@@ -404,7 +411,7 @@ export class CallsServicePeer extends Resource {
|
|
|
404
411
|
})
|
|
405
412
|
.catch((err) => log.catch(err));
|
|
406
413
|
} else {
|
|
407
|
-
log.
|
|
414
|
+
log.info('missing track info', { trackData, availableTracks: Array.from(trackMap.keys()) });
|
|
408
415
|
}
|
|
409
416
|
});
|
|
410
417
|
|
|
@@ -528,3 +535,47 @@ const peerConnectionIsConnected = async (peerConnection: RTCPeerConnection) => {
|
|
|
528
535
|
await connected;
|
|
529
536
|
}
|
|
530
537
|
};
|
|
538
|
+
|
|
539
|
+
const INITIAL_DELAY = 50;
|
|
540
|
+
const MAX_DELAY = 500;
|
|
541
|
+
const MAX_RETRIES = 20;
|
|
542
|
+
const WAIT_FOR_DATA_TIMEOUT = 10_000;
|
|
543
|
+
|
|
544
|
+
const waitForTransceiverToSendData = async (ctx: Context, transceiver: RTCRtpTransceiver): Promise<void> => {
|
|
545
|
+
let delay = INITIAL_DELAY;
|
|
546
|
+
let checks = 0;
|
|
547
|
+
const waitForData = new Trigger();
|
|
548
|
+
ctx.onDispose(() => waitForData.throw(new Error('Transceiver was closed')));
|
|
549
|
+
|
|
550
|
+
const checkStats = async () => {
|
|
551
|
+
if (ctx.disposed) return;
|
|
552
|
+
|
|
553
|
+
try {
|
|
554
|
+
const stats = await transceiver.sender.getStats();
|
|
555
|
+
let dataFound = false;
|
|
556
|
+
stats.forEach((stat) => {
|
|
557
|
+
if (stat.type === 'outbound-rtp' && stat.bytesSent > 0) {
|
|
558
|
+
dataFound = true;
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
if (dataFound) {
|
|
563
|
+
waitForData.wake();
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
} catch {
|
|
567
|
+
// Stats might not be available yet, continue checking
|
|
568
|
+
}
|
|
569
|
+
checks++;
|
|
570
|
+
if (checks > MAX_RETRIES) {
|
|
571
|
+
waitForData.throw(new Error('Transceiver did not send data'));
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
delay = Math.min(delay * 2, MAX_DELAY); // Exponential backoff with max cap
|
|
576
|
+
scheduleTask(ctx, checkStats, delay);
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
void checkStats();
|
|
580
|
+
return waitForData.wait({ timeout: WAIT_FOR_DATA_TIMEOUT });
|
|
581
|
+
};
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
//
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { scheduleTaskInterval, waitForCondition } from '@dxos/async';
|
|
7
|
+
import { type Context, cancelWithContext } from '@dxos/context';
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -30,7 +30,7 @@ export const createBlackCanvasStreamTrack = async ({
|
|
|
30
30
|
canvasCtx.fillStyle = 'black';
|
|
31
31
|
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
|
|
32
32
|
};
|
|
33
|
-
scheduleTaskInterval(ctx, async () => drawFrame(),
|
|
33
|
+
scheduleTaskInterval(ctx, async () => drawFrame(), 200);
|
|
34
34
|
|
|
35
35
|
const track = canvas.captureStream().getVideoTracks()[0];
|
|
36
36
|
drawFrame();
|
|
@@ -40,6 +40,20 @@ export const createBlackCanvasStreamTrack = async ({
|
|
|
40
40
|
return track;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
let audioContextStartedPreviously = false;
|
|
44
|
+
const userGestureEvents = [
|
|
45
|
+
'click',
|
|
46
|
+
'contextmenu',
|
|
47
|
+
'auxclick',
|
|
48
|
+
'dblclick',
|
|
49
|
+
'mousedown',
|
|
50
|
+
'mouseup',
|
|
51
|
+
'pointerup',
|
|
52
|
+
'touchend',
|
|
53
|
+
'keydown',
|
|
54
|
+
'keyup',
|
|
55
|
+
];
|
|
56
|
+
|
|
43
57
|
/**
|
|
44
58
|
* Creates an inaudible audio stream track.
|
|
45
59
|
* We need this to have audio track available when audio is disabled.
|
|
@@ -53,14 +67,57 @@ export const createInaudibleAudioStreamTrack = async ({ ctx }: { ctx: Context })
|
|
|
53
67
|
|
|
54
68
|
const gainNode = audioContext.createGain();
|
|
55
69
|
// even w/ gain at 0 some packets are sent
|
|
56
|
-
gainNode.gain.setValueAtTime(0
|
|
70
|
+
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
|
|
57
71
|
oscillator.connect(gainNode);
|
|
58
72
|
|
|
59
73
|
const destination = audioContext.createMediaStreamDestination();
|
|
60
74
|
gainNode.connect(destination);
|
|
61
|
-
|
|
75
|
+
|
|
76
|
+
let oscillatorStarted = false;
|
|
77
|
+
const ensureOscillatorStarted = () => {
|
|
78
|
+
if (oscillatorStarted) return;
|
|
79
|
+
oscillator.start();
|
|
80
|
+
oscillatorStarted = true;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const stateChangeHandler = () => {
|
|
84
|
+
if (audioContext.state === 'running') {
|
|
85
|
+
audioContextStartedPreviously = true;
|
|
86
|
+
ensureOscillatorStarted();
|
|
87
|
+
}
|
|
88
|
+
if (audioContext.state === 'suspended' || (audioContext.state as string) === 'interrupted') {
|
|
89
|
+
resumeAudioContext();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
audioContext.addEventListener('statechange', stateChangeHandler);
|
|
94
|
+
|
|
95
|
+
const resumeAudioContext = () => {
|
|
96
|
+
void audioContext.resume().then(() => {
|
|
97
|
+
cleanUpUserGestureListeners();
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const cleanUpUserGestureListeners = () => {
|
|
102
|
+
userGestureEvents.forEach((gesture) => {
|
|
103
|
+
document.removeEventListener(gesture, resumeAudioContext, {
|
|
104
|
+
capture: true,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (audioContextStartedPreviously) {
|
|
110
|
+
resumeAudioContext();
|
|
111
|
+
} else {
|
|
112
|
+
userGestureEvents.forEach((gesture) => {
|
|
113
|
+
document.addEventListener(gesture, resumeAudioContext, {
|
|
114
|
+
capture: true,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
62
118
|
|
|
63
119
|
ctx.onDispose(async () => {
|
|
120
|
+
cleanUpUserGestureListeners();
|
|
64
121
|
oscillator.disconnect();
|
|
65
122
|
oscillator.stop();
|
|
66
123
|
await audioContext.close();
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
6
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
8
7
|
|
|
9
8
|
import { scheduleTask, sleep } from '@dxos/async';
|
|
@@ -13,14 +12,15 @@ import { log } from '@dxos/log';
|
|
|
13
12
|
import { Config, useConfig } from '@dxos/react-client';
|
|
14
13
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
15
14
|
import { Button } from '@dxos/react-ui';
|
|
15
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
16
16
|
import { Json } from '@dxos/react-ui-syntax-highlighter';
|
|
17
|
-
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import
|
|
18
|
+
const testVideo = new URL('../testing/video.mp4', import.meta.url).href;
|
|
19
|
+
|
|
20
|
+
import { CALLS_URL, CallsServicePeer, type TrackObject } from './calls';
|
|
21
|
+
import { useBlackCanvasStreamTrack, useInaudibleAudioStreamTrack, useVideoStreamTrack } from './hooks';
|
|
23
22
|
|
|
23
|
+
// TODO(burdon): THIS IS TOO COMPLEX FOR A TEST SETUP.
|
|
24
24
|
const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
|
|
25
25
|
const config = useConfig();
|
|
26
26
|
const callsConfig = {
|
|
@@ -35,16 +35,18 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
|
|
|
35
35
|
const [pulledTrack, setPulledTrack] = useState<MediaStreamTrack | undefined>(undefined);
|
|
36
36
|
const hadRun = useRef(false);
|
|
37
37
|
|
|
38
|
+
const deps: any[] = [];
|
|
38
39
|
// Push/pull video stream track to cloudflare.
|
|
39
40
|
useEffect(() => {
|
|
40
41
|
if (hadRun.current || !mediaStreamTrack || !peerPush || !peerPull) {
|
|
41
|
-
log.info('not running', { hadRun: hadRun.current, mediaStreamTrack, peerPush, peerPull });
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
hadRun.current = true;
|
|
45
45
|
const ctx = Context.default();
|
|
46
|
+
log.info('setting up ctx', { ctx, mediaStreamTrack, peerPush, peerPull });
|
|
47
|
+
deps.push([ctx, mediaStreamTrack, peerPush, peerPull]);
|
|
46
48
|
ctx.onDispose(() => {
|
|
47
|
-
log.info('disposing ctx', {
|
|
49
|
+
log.info('disposing ctx', {});
|
|
48
50
|
});
|
|
49
51
|
scheduleTask(
|
|
50
52
|
ctx,
|
|
@@ -60,15 +62,12 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
|
|
|
60
62
|
// Push track to cloudflare.
|
|
61
63
|
performance.mark('push:begin');
|
|
62
64
|
log.info('pushing track', { track: mediaStreamTrack });
|
|
63
|
-
trackInfo.current = await peerPush.pushTrack({ track: mediaStreamTrack });
|
|
65
|
+
trackInfo.current = await peerPush.pushTrack({ track: mediaStreamTrack, ctx: Context.default() });
|
|
64
66
|
performance.mark('push:end');
|
|
65
67
|
const pushTime = performance.measure('push', 'push:begin', 'push:end').duration;
|
|
66
68
|
log.info('successfully pushed track', { pushTime, trackInfo: trackInfo.current });
|
|
67
69
|
setMetrics((prev) => ({ ...prev, 'time to push track [ms]': Math.round(pushTime) }));
|
|
68
70
|
|
|
69
|
-
// Wait for cloudflare to process the track.
|
|
70
|
-
await sleep(1000);
|
|
71
|
-
|
|
72
71
|
// Pull track from cloudflare.
|
|
73
72
|
performance.mark('pullTrack:begin');
|
|
74
73
|
pullCtx.current = Context.default();
|
|
@@ -104,7 +103,10 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
|
|
|
104
103
|
|
|
105
104
|
const rePullTrack = useCallback(async () => {
|
|
106
105
|
performance.mark('rePullVideo:begin');
|
|
107
|
-
|
|
106
|
+
if (!pulledTrack) {
|
|
107
|
+
log.info('no pulled track, skipping re-pull', { pulledTrack });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
108
110
|
pulledTrack.stop();
|
|
109
111
|
await pullCtx.current!.dispose();
|
|
110
112
|
pullCtx.current = Context.default();
|
|
@@ -123,12 +125,57 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
|
|
|
123
125
|
pulledTrack,
|
|
124
126
|
rePullTrack,
|
|
125
127
|
metrics,
|
|
128
|
+
pushService: peerPush,
|
|
129
|
+
pullService: peerPull,
|
|
126
130
|
};
|
|
127
131
|
};
|
|
128
132
|
|
|
133
|
+
type StoryProps = { source: string };
|
|
134
|
+
|
|
135
|
+
const DefaultStory = ({ source }: StoryProps) => {
|
|
136
|
+
const pushVideoElement = useRef<HTMLVideoElement>(null);
|
|
137
|
+
const pullVideoElement = useRef<HTMLVideoElement>(null);
|
|
138
|
+
// Get video stream track.
|
|
139
|
+
const videoStreamTrack = useVideoStreamTrack(pushVideoElement, source);
|
|
140
|
+
const { pulledTrack, rePullTrack, metrics } = pushAndPullTrack(videoStreamTrack);
|
|
141
|
+
const hadRun = useRef(false);
|
|
142
|
+
|
|
143
|
+
// Push/pull video stream track to cloudflare.
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!pulledTrack || hadRun.current) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
hadRun.current = true;
|
|
149
|
+
const ctx = Context.default();
|
|
150
|
+
scheduleTask(ctx, async () => {
|
|
151
|
+
pullVideoElement.current!.srcObject = new MediaStream([pulledTrack]);
|
|
152
|
+
await pullVideoElement.current!.play();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
void ctx.dispose();
|
|
157
|
+
};
|
|
158
|
+
}, [pulledTrack]);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div className='grid grid-rows-3 gap-4 items-center'>
|
|
162
|
+
<video ref={pushVideoElement} muted autoPlay loop />
|
|
163
|
+
<div className='flex flex-col gap-4'>
|
|
164
|
+
<video ref={pullVideoElement} muted />
|
|
165
|
+
<Button disabled={!pulledTrack} onClick={rePullTrack}>
|
|
166
|
+
Re-pull video
|
|
167
|
+
</Button>
|
|
168
|
+
</div>
|
|
169
|
+
<Json data={metrics} />
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
129
174
|
const meta = {
|
|
130
175
|
title: 'plugins/plugin-thread/calls-service',
|
|
176
|
+
render: DefaultStory,
|
|
131
177
|
decorators: [
|
|
178
|
+
withTheme,
|
|
132
179
|
withClientProvider({
|
|
133
180
|
config: new Config({
|
|
134
181
|
runtime: {
|
|
@@ -138,72 +185,25 @@ const meta = {
|
|
|
138
185
|
},
|
|
139
186
|
}),
|
|
140
187
|
}),
|
|
141
|
-
withTheme,
|
|
142
|
-
withLayout(),
|
|
143
188
|
],
|
|
144
189
|
parameters: {
|
|
145
190
|
layout: 'centered',
|
|
146
191
|
},
|
|
147
|
-
}
|
|
192
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
148
193
|
|
|
149
194
|
export default meta;
|
|
150
195
|
|
|
151
|
-
|
|
152
|
-
render: ({ videoSrc }: { videoSrc: string }) => {
|
|
153
|
-
const pushVideoElement = useRef<HTMLVideoElement>(null);
|
|
154
|
-
const pullVideoElement = useRef<HTMLVideoElement>(null);
|
|
155
|
-
// Get video stream track.
|
|
156
|
-
const videoStreamTrack = useVideoStreamTrack(pushVideoElement, videoSrc);
|
|
157
|
-
const { pulledTrack, rePullTrack, metrics } = pushAndPullTrack(videoStreamTrack);
|
|
158
|
-
const hadRun = useRef(false);
|
|
159
|
-
|
|
160
|
-
// Push/pull video stream track to cloudflare.
|
|
161
|
-
useEffect(() => {
|
|
162
|
-
if (!pulledTrack || hadRun.current) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
hadRun.current = true;
|
|
166
|
-
const ctx = Context.default();
|
|
167
|
-
scheduleTask(ctx, async () => {
|
|
168
|
-
pullVideoElement.current!.srcObject = new MediaStream([pulledTrack]);
|
|
169
|
-
await pullVideoElement.current!.play();
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
return () => {
|
|
173
|
-
void ctx.dispose();
|
|
174
|
-
};
|
|
175
|
-
}, [pulledTrack]);
|
|
196
|
+
type Story = StoryObj<typeof meta>;
|
|
176
197
|
|
|
177
|
-
|
|
178
|
-
<div className='grid grid-rows-3 gap-4 items-center'>
|
|
179
|
-
<video ref={pushVideoElement} muted autoPlay loop />
|
|
180
|
-
<div className='flex flex-col gap-4'>
|
|
181
|
-
<video ref={pullVideoElement} muted />
|
|
182
|
-
<Button
|
|
183
|
-
disabled={
|
|
184
|
-
!(
|
|
185
|
-
pullVideoElement?.current?.srcObject instanceof MediaStream &&
|
|
186
|
-
pullVideoElement.current.srcObject.getTracks()[0]
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
onClick={rePullTrack}
|
|
190
|
-
>
|
|
191
|
-
Re-pull video
|
|
192
|
-
</Button>
|
|
193
|
-
</div>
|
|
194
|
-
<Json data={metrics} />
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
},
|
|
198
|
+
export const Default: Story = {
|
|
198
199
|
args: {
|
|
199
|
-
|
|
200
|
+
source: testVideo,
|
|
200
201
|
},
|
|
201
202
|
};
|
|
202
203
|
|
|
203
204
|
export const InaudibleAudioStreamTrack = {
|
|
204
205
|
render: () => {
|
|
205
206
|
const audioStreamTrack = useInaudibleAudioStreamTrack();
|
|
206
|
-
|
|
207
207
|
const { rePullTrack, metrics } = pushAndPullTrack(audioStreamTrack);
|
|
208
208
|
|
|
209
209
|
return (
|
|
@@ -214,3 +214,16 @@ export const InaudibleAudioStreamTrack = {
|
|
|
214
214
|
);
|
|
215
215
|
},
|
|
216
216
|
};
|
|
217
|
+
|
|
218
|
+
export const BlackVideoStreamTrack = {
|
|
219
|
+
render: () => {
|
|
220
|
+
const videoStreamTrack = useBlackCanvasStreamTrack();
|
|
221
|
+
const { rePullTrack, metrics } = pushAndPullTrack(videoStreamTrack);
|
|
222
|
+
return (
|
|
223
|
+
<div className='flex flex-col gap-4 items-center'>
|
|
224
|
+
<Json data={metrics} />
|
|
225
|
+
<Button onClick={rePullTrack}>Re-pull audio</Button>
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
};
|
|
@@ -3,30 +3,34 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Rx } from '@effect-rx/rx-react';
|
|
6
|
-
import
|
|
6
|
+
import * as Function from 'effect/Function';
|
|
7
|
+
import * as Option from 'effect/Option';
|
|
7
8
|
|
|
8
|
-
import { Capabilities, contributes, createIntent
|
|
9
|
+
import { Capabilities, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
|
|
9
10
|
import { Obj } from '@dxos/echo';
|
|
10
11
|
import { AttentionCapabilities } from '@dxos/plugin-attention';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
12
|
+
import { ATTENDABLE_PATH_SEPARATOR, DECK_COMPANION_TYPE, PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
|
|
13
|
+
import { ROOT_ID, createExtension, rxFromSignal } from '@dxos/plugin-graph';
|
|
13
14
|
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
-
import { meta, THREAD_PLUGIN } from '../meta';
|
|
16
|
+
import { meta } from '../meta';
|
|
17
17
|
import { ChannelType, ThreadAction } from '../types';
|
|
18
18
|
import { getAnchor } from '../util';
|
|
19
19
|
|
|
20
|
+
import { ThreadCapabilities } from './capabilities';
|
|
21
|
+
|
|
22
|
+
// TODO(wittjosiah): Highlight active calls in L1.
|
|
23
|
+
// Track active meetings by subscribing to meetings query and polling the swarms of recent meetings in the space.
|
|
20
24
|
export default (context: PluginContext) => {
|
|
21
25
|
const resolve = (typename: string) =>
|
|
22
26
|
context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {};
|
|
23
27
|
|
|
24
28
|
return contributes(Capabilities.AppGraphBuilder, [
|
|
25
29
|
createExtension({
|
|
26
|
-
id: `${
|
|
30
|
+
id: `${meta.id}/active-call`,
|
|
27
31
|
connector: (node) =>
|
|
28
32
|
Rx.make((get) =>
|
|
29
|
-
pipe(
|
|
33
|
+
Function.pipe(
|
|
30
34
|
get(node),
|
|
31
35
|
Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
|
|
32
36
|
Option.map((node) => {
|
|
@@ -40,7 +44,7 @@ export default (context: PluginContext) => {
|
|
|
40
44
|
type: DECK_COMPANION_TYPE,
|
|
41
45
|
data: null,
|
|
42
46
|
properties: {
|
|
43
|
-
label: ['call panel label', { ns:
|
|
47
|
+
label: ['call panel label', { ns: meta.id }],
|
|
44
48
|
icon: 'ph--video-conference--regular',
|
|
45
49
|
position: 'hoist',
|
|
46
50
|
disposition: 'hidden',
|
|
@@ -58,10 +62,10 @@ export default (context: PluginContext) => {
|
|
|
58
62
|
// TODO(wittjosiah): The channel shouldn't become the companion during a call.
|
|
59
63
|
// Alternative: the call/meeting/thread should be a child node of the channel and that should be opened.
|
|
60
64
|
createExtension({
|
|
61
|
-
id: `${
|
|
65
|
+
id: `${meta.id}/channel-chat-companion`,
|
|
62
66
|
connector: (node) => {
|
|
63
67
|
return Rx.make((get) => {
|
|
64
|
-
return pipe(
|
|
68
|
+
return Function.pipe(
|
|
65
69
|
get(node),
|
|
66
70
|
Option.flatMap((node) => (Obj.instanceOf(ChannelType, node.data) ? Option.some(node.data) : Option.none())),
|
|
67
71
|
Option.map((channel) => {
|
|
@@ -79,7 +83,7 @@ export default (context: PluginContext) => {
|
|
|
79
83
|
type: PLANK_COMPANION_TYPE,
|
|
80
84
|
data: 'chat',
|
|
81
85
|
properties: {
|
|
82
|
-
label: ['channel companion label', { ns:
|
|
86
|
+
label: ['channel companion label', { ns: meta.id }],
|
|
83
87
|
icon: 'ph--hash--regular',
|
|
84
88
|
position: 'hoist',
|
|
85
89
|
disposition: 'hidden',
|
|
@@ -96,7 +100,7 @@ export default (context: PluginContext) => {
|
|
|
96
100
|
id: `${meta.id}/comments-companion`,
|
|
97
101
|
connector: (node) =>
|
|
98
102
|
Rx.make((get) =>
|
|
99
|
-
pipe(
|
|
103
|
+
Function.pipe(
|
|
100
104
|
get(node),
|
|
101
105
|
Option.flatMap((node) => {
|
|
102
106
|
if (!Obj.isObject(node.data) || Obj.instanceOf(ChannelType, node.data)) {
|
|
@@ -126,7 +130,7 @@ export default (context: PluginContext) => {
|
|
|
126
130
|
id: `${meta.id}/comment-toolbar`,
|
|
127
131
|
actions: (node) =>
|
|
128
132
|
Rx.make((get) =>
|
|
129
|
-
pipe(
|
|
133
|
+
Function.pipe(
|
|
130
134
|
get(node),
|
|
131
135
|
Option.flatMap((node) => {
|
|
132
136
|
if (!Obj.isObject(node.data) || Obj.instanceOf(ChannelType, node.data)) {
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type PluginContext, contributes } from '@dxos/app-framework';
|
|
6
6
|
import { ClientCapabilities } from '@dxos/plugin-client';
|
|
7
7
|
|
|
8
|
-
import { ThreadCapabilities } from './capabilities';
|
|
9
8
|
import { CallManager } from '../calls';
|
|
10
9
|
|
|
10
|
+
import { ThreadCapabilities } from './capabilities';
|
|
11
|
+
|
|
11
12
|
export default async (context: PluginContext) => {
|
|
12
13
|
const client = context.getCapability(ClientCapabilities.Client);
|
|
13
14
|
const callManager = new CallManager(client);
|