@dxos/plugin-thread 0.8.2-main.fbd8ed0 → 0.8.2-staging.42af850

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 (385) 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-32EIWOAL.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 +101 -35
  28. package/dist/lib/browser/index.mjs.map +4 -4
  29. package/dist/lib/browser/{intent-resolver-PDOLX5B7.mjs → intent-resolver-YUYZCTLF.mjs} +95 -62
  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-K56QXNJ4.mjs → settings-KVBQQSO5.mjs} +3 -3
  39. package/dist/lib/browser/{state-KBGI3QTR.mjs → state-RKL5MSDP.mjs} +4 -4
  40. package/dist/lib/browser/{state-KBGI3QTR.mjs.map → state-RKL5MSDP.mjs.map} +3 -3
  41. package/dist/lib/browser/types/index.mjs +16 -0
  42. package/dist/lib/browser/types/index.mjs.map +7 -0
  43. package/dist/lib/node/ChannelContainer-5EKNEH56.cjs +36 -0
  44. package/dist/lib/node/ChannelContainer-5EKNEH56.cjs.map +7 -0
  45. package/dist/lib/node/ChatContainer-EE3FPSYO.cjs +35 -0
  46. package/dist/lib/node/ChatContainer-EE3FPSYO.cjs.map +7 -0
  47. package/dist/lib/node/app-graph-builder-IRE3GYKE.cjs +179 -0
  48. package/dist/lib/node/app-graph-builder-IRE3GYKE.cjs.map +7 -0
  49. package/dist/lib/node/call-manager-HV2XJW5O.cjs +37 -0
  50. package/dist/lib/node/call-manager-HV2XJW5O.cjs.map +7 -0
  51. package/dist/lib/node/chunk-44I33JDT.cjs +1731 -0
  52. package/dist/lib/node/chunk-44I33JDT.cjs.map +7 -0
  53. package/dist/lib/node/chunk-4B3PPXA6.cjs +371 -0
  54. package/dist/lib/node/chunk-4B3PPXA6.cjs.map +7 -0
  55. package/dist/lib/node/{chunk-NQ5DTPOY.cjs → chunk-4YSNVPWU.cjs} +10 -8
  56. package/dist/lib/node/chunk-4YSNVPWU.cjs.map +7 -0
  57. package/dist/lib/node/chunk-6B3M6XRE.cjs +984 -0
  58. package/dist/lib/node/chunk-6B3M6XRE.cjs.map +7 -0
  59. package/dist/lib/node/{chunk-NO6WQR36.cjs → chunk-FUXJAVFZ.cjs} +50 -29
  60. package/dist/lib/node/chunk-FUXJAVFZ.cjs.map +7 -0
  61. package/dist/lib/node/{chunk-ONLWOUYZ.cjs → chunk-HLMCEE5K.cjs} +7 -7
  62. package/dist/lib/node/{chunk-ONLWOUYZ.cjs.map → chunk-HLMCEE5K.cjs.map} +2 -2
  63. package/dist/lib/node/chunk-JLHENFGK.cjs +549 -0
  64. package/dist/lib/node/chunk-JLHENFGK.cjs.map +7 -0
  65. package/dist/lib/node/chunk-LR5NENBE.cjs +54 -0
  66. package/dist/lib/node/chunk-LR5NENBE.cjs.map +7 -0
  67. package/dist/lib/node/chunk-TKHJVC2C.cjs +147 -0
  68. package/dist/lib/node/chunk-TKHJVC2C.cjs.map +7 -0
  69. package/dist/lib/node/{chunk-AZ275P4V.cjs → chunk-U7NOIXN6.cjs} +16 -10
  70. package/dist/lib/node/chunk-U7NOIXN6.cjs.map +7 -0
  71. package/dist/lib/node/index.cjs +136 -74
  72. package/dist/lib/node/index.cjs.map +4 -4
  73. package/dist/lib/node/{intent-resolver-MNTL32W2.cjs → intent-resolver-MZ3HADNY.cjs} +107 -76
  74. package/dist/lib/node/intent-resolver-MZ3HADNY.cjs.map +7 -0
  75. package/dist/lib/node/markdown-NJDPXIKD.cjs +178 -0
  76. package/dist/lib/node/markdown-NJDPXIKD.cjs.map +7 -0
  77. package/dist/lib/node/meta.json +1 -1
  78. package/dist/lib/node/react-root-IAYQ2DBO.cjs +51 -0
  79. package/dist/lib/node/react-root-IAYQ2DBO.cjs.map +7 -0
  80. package/dist/lib/node/react-surface-AVC5VVIS.cjs +122 -0
  81. package/dist/lib/node/react-surface-AVC5VVIS.cjs.map +7 -0
  82. package/dist/lib/node/{settings-4WLSO726.cjs → settings-H45P5YO2.cjs} +8 -8
  83. package/dist/lib/node/{state-FOHMAVSU.cjs → state-IY5XX3H2.cjs} +8 -8
  84. package/dist/lib/node/{state-FOHMAVSU.cjs.map → state-IY5XX3H2.cjs.map} +3 -3
  85. package/dist/lib/node/{types.cjs → types/index.cjs} +12 -6
  86. package/dist/lib/node/types/index.cjs.map +7 -0
  87. package/dist/lib/node-esm/ChannelContainer-KFS2RWWW.mjs +16 -0
  88. package/dist/lib/node-esm/ChannelContainer-KFS2RWWW.mjs.map +7 -0
  89. package/dist/lib/node-esm/ChatContainer-UJYPQMLV.mjs +15 -0
  90. package/dist/lib/node-esm/ChatContainer-UJYPQMLV.mjs.map +7 -0
  91. package/dist/lib/node-esm/app-graph-builder-I5VMPLE4.mjs +172 -0
  92. package/dist/lib/node-esm/app-graph-builder-I5VMPLE4.mjs.map +7 -0
  93. package/dist/lib/node-esm/call-manager-UUYXLF4T.mjs +24 -0
  94. package/dist/lib/node-esm/call-manager-UUYXLF4T.mjs.map +7 -0
  95. package/dist/lib/node-esm/{chunk-42F2DTMM.mjs → chunk-ESQC6SYE.mjs} +4 -4
  96. package/dist/lib/node-esm/chunk-ESQC6SYE.mjs.map +7 -0
  97. package/dist/lib/node-esm/chunk-J323VQ4P.mjs +127 -0
  98. package/dist/lib/node-esm/chunk-J323VQ4P.mjs.map +7 -0
  99. package/dist/lib/node-esm/{chunk-WLY2HMDB.mjs → chunk-K4KAUOKT.mjs} +5 -3
  100. package/dist/lib/node-esm/chunk-K4KAUOKT.mjs.map +7 -0
  101. package/dist/lib/node-esm/chunk-MNJ3V4XM.mjs +546 -0
  102. package/dist/lib/node-esm/chunk-MNJ3V4XM.mjs.map +7 -0
  103. package/dist/lib/node-esm/{chunk-IZWN4KAY.mjs → chunk-O3V45YJP.mjs} +57 -35
  104. package/dist/lib/node-esm/chunk-O3V45YJP.mjs.map +7 -0
  105. package/dist/lib/node-esm/chunk-ODUB4GAB.mjs +23 -0
  106. package/dist/lib/node-esm/chunk-ODUB4GAB.mjs.map +7 -0
  107. package/dist/lib/node-esm/chunk-PGRP3O4O.mjs +33 -0
  108. package/dist/lib/node-esm/chunk-PGRP3O4O.mjs.map +7 -0
  109. package/dist/lib/node-esm/chunk-POKXRTII.mjs +352 -0
  110. package/dist/lib/node-esm/chunk-POKXRTII.mjs.map +7 -0
  111. package/dist/lib/node-esm/chunk-WXNPD542.mjs +1710 -0
  112. package/dist/lib/node-esm/chunk-WXNPD542.mjs.map +7 -0
  113. package/dist/lib/node-esm/chunk-X6AUQDTE.mjs +984 -0
  114. package/dist/lib/node-esm/chunk-X6AUQDTE.mjs.map +7 -0
  115. package/dist/lib/node-esm/index.mjs +101 -35
  116. package/dist/lib/node-esm/index.mjs.map +4 -4
  117. package/dist/lib/node-esm/{intent-resolver-X2FNKFSK.mjs → intent-resolver-CU22OPAH.mjs} +95 -62
  118. package/dist/lib/node-esm/intent-resolver-CU22OPAH.mjs.map +7 -0
  119. package/dist/lib/node-esm/markdown-HRGGO7IA.mjs +170 -0
  120. package/dist/lib/node-esm/markdown-HRGGO7IA.mjs.map +7 -0
  121. package/dist/lib/node-esm/meta.json +1 -1
  122. package/dist/lib/node-esm/react-root-NQMUEI6Z.mjs +28 -0
  123. package/dist/lib/node-esm/react-root-NQMUEI6Z.mjs.map +7 -0
  124. package/dist/lib/node-esm/react-surface-Z4X5VLMY.mjs +109 -0
  125. package/dist/lib/node-esm/react-surface-Z4X5VLMY.mjs.map +7 -0
  126. package/dist/lib/node-esm/{settings-5UA6FBMW.mjs → settings-IJGUOU3F.mjs} +3 -3
  127. package/dist/lib/node-esm/{state-IRLEO2SB.mjs → state-6AUTNROZ.mjs} +4 -4
  128. package/dist/lib/node-esm/{state-IRLEO2SB.mjs.map → state-6AUTNROZ.mjs.map} +3 -3
  129. package/dist/lib/node-esm/types/index.mjs +17 -0
  130. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  131. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  132. package/dist/types/src/calls/call-manager.d.ts +77 -0
  133. package/dist/types/src/calls/call-manager.d.ts.map +1 -0
  134. package/dist/types/src/calls/call-swarm-synchronizer.d.ts +66 -0
  135. package/dist/types/src/calls/call-swarm-synchronizer.d.ts.map +1 -0
  136. package/dist/types/src/calls/index.d.ts +7 -0
  137. package/dist/types/src/calls/index.d.ts.map +1 -0
  138. package/dist/types/src/calls/media-manager.d.ts +60 -0
  139. package/dist/types/src/calls/media-manager.d.ts.map +1 -0
  140. package/dist/types/src/calls/speaking-monitor.d.ts +24 -0
  141. package/dist/types/src/calls/speaking-monitor.d.ts.map +1 -0
  142. package/dist/types/src/calls/types.d.ts +45 -0
  143. package/dist/types/src/calls/types.d.ts.map +1 -0
  144. package/dist/types/src/calls/util/audio.d.ts +6 -0
  145. package/dist/types/src/calls/util/audio.d.ts.map +1 -0
  146. package/dist/types/src/calls/util/calls-service.d.ts +72 -0
  147. package/dist/types/src/calls/util/calls-service.d.ts.map +1 -0
  148. package/dist/types/src/calls/util/history-cache.d.ts +11 -0
  149. package/dist/types/src/calls/util/history-cache.d.ts.map +1 -0
  150. package/dist/types/src/calls/util/index.d.ts +5 -0
  151. package/dist/types/src/calls/util/index.d.ts.map +1 -0
  152. package/dist/types/src/calls/util/media.d.ts +6 -0
  153. package/dist/types/src/calls/util/media.d.ts.map +1 -0
  154. package/dist/types/src/calls/util/stub-media-streams.d.ts +18 -0
  155. package/dist/types/src/calls/util/stub-media-streams.d.ts.map +1 -0
  156. package/dist/types/src/calls/util/task-scheduling.d.ts +17 -0
  157. package/dist/types/src/calls/util/task-scheduling.d.ts.map +1 -0
  158. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  159. package/dist/types/src/capabilities/call-manager.d.ts +5 -0
  160. package/dist/types/src/capabilities/call-manager.d.ts.map +1 -0
  161. package/dist/types/src/capabilities/capabilities.d.ts +13 -1
  162. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  163. package/dist/types/src/capabilities/index.d.ts +5 -0
  164. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  165. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  166. package/dist/types/src/capabilities/markdown.d.ts.map +1 -1
  167. package/dist/types/src/capabilities/react-root.d.ts +7 -0
  168. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  169. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  170. package/dist/types/src/components/Call/Call.d.ts +17 -0
  171. package/dist/types/src/components/Call/Call.d.ts.map +1 -0
  172. package/dist/types/src/components/Call/Toolbar.d.ts +16 -0
  173. package/dist/types/src/components/Call/Toolbar.d.ts.map +1 -0
  174. package/dist/types/src/components/Call/index.d.ts +3 -0
  175. package/dist/types/src/components/Call/index.d.ts.map +1 -0
  176. package/dist/types/src/components/CallDebugPanel.d.ts +8 -0
  177. package/dist/types/src/components/CallDebugPanel.d.ts.map +1 -0
  178. package/dist/types/src/components/CallSidebar.d.ts +4 -0
  179. package/dist/types/src/components/CallSidebar.d.ts.map +1 -0
  180. package/dist/types/src/components/ChannelContainer.d.ts +14 -0
  181. package/dist/types/src/components/ChannelContainer.d.ts.map +1 -0
  182. package/dist/types/src/components/ChannelContainer.stories.d.ts +10 -0
  183. package/dist/types/src/components/ChannelContainer.stories.d.ts.map +1 -0
  184. package/dist/types/src/components/ChatContainer.d.ts +9 -7
  185. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  186. package/dist/types/src/components/CommentContainer.d.ts +11 -2
  187. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  188. package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
  189. package/dist/types/src/components/CommentsContainer.d.ts +5 -9
  190. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  191. package/dist/types/src/components/Lobby/Lobby.d.ts +15 -0
  192. package/dist/types/src/components/Lobby/Lobby.d.ts.map +1 -0
  193. package/dist/types/src/components/Lobby/index.d.ts +2 -0
  194. package/dist/types/src/components/Lobby/index.d.ts.map +1 -0
  195. package/dist/types/src/components/Media/AudioStream.d.ts +6 -0
  196. package/dist/types/src/components/Media/AudioStream.d.ts.map +1 -0
  197. package/dist/types/src/components/Media/VideoObject.d.ts +15 -0
  198. package/dist/types/src/components/Media/VideoObject.d.ts.map +1 -0
  199. package/dist/types/src/components/Media/VideoObject.stories.d.ts +8 -0
  200. package/dist/types/src/components/Media/VideoObject.stories.d.ts.map +1 -0
  201. package/dist/types/src/components/Media/index.d.ts +3 -0
  202. package/dist/types/src/components/Media/index.d.ts.map +1 -0
  203. package/dist/types/src/components/Participant/Participant.d.ts +6 -0
  204. package/dist/types/src/components/Participant/Participant.d.ts.map +1 -0
  205. package/dist/types/src/components/Participant/ParticipantGrid.d.ts +11 -0
  206. package/dist/types/src/components/Participant/ParticipantGrid.d.ts.map +1 -0
  207. package/dist/types/src/components/Participant/index.d.ts +3 -0
  208. package/dist/types/src/components/Participant/index.d.ts.map +1 -0
  209. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.d.ts +12 -0
  210. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.d.ts.map +1 -0
  211. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts +10 -0
  212. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts.map +1 -0
  213. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.d.ts +32 -0
  214. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.d.ts.map +1 -0
  215. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts +23 -0
  216. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts.map +1 -0
  217. package/dist/types/src/components/ResponsiveGrid/ResponsiveGridItem.d.ts +21 -0
  218. package/dist/types/src/components/ResponsiveGrid/ResponsiveGridItem.d.ts.map +1 -0
  219. package/dist/types/src/components/ResponsiveGrid/index.d.ts +4 -0
  220. package/dist/types/src/components/ResponsiveGrid/index.d.ts.map +1 -0
  221. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
  222. package/dist/types/src/components/index.d.ts +5 -1
  223. package/dist/types/src/components/index.d.ts.map +1 -1
  224. package/dist/types/src/components/testing.d.ts +14 -14
  225. package/dist/types/src/components/testing.d.ts.map +1 -1
  226. package/dist/types/src/extensions/threads.d.ts.map +1 -1
  227. package/dist/types/src/hooks/index.d.ts +2 -1
  228. package/dist/types/src/hooks/index.d.ts.map +1 -1
  229. package/dist/types/src/hooks/useDebugMode.d.ts +2 -0
  230. package/dist/types/src/hooks/useDebugMode.d.ts.map +1 -0
  231. package/dist/types/src/index.d.ts +1 -1
  232. package/dist/types/src/index.d.ts.map +1 -1
  233. package/dist/types/src/meta.d.ts.map +1 -1
  234. package/dist/types/src/peers.stories.d.ts +11 -0
  235. package/dist/types/src/peers.stories.d.ts.map +1 -0
  236. package/dist/types/src/testing/index.d.ts +2 -0
  237. package/dist/types/src/testing/index.d.ts.map +1 -0
  238. package/dist/types/src/testing/stories.d.ts +2 -0
  239. package/dist/types/src/testing/stories.d.ts.map +1 -0
  240. package/dist/types/src/translations.d.ts +34 -1
  241. package/dist/types/src/translations.d.ts.map +1 -1
  242. package/dist/types/src/types/index.d.ts +3 -0
  243. package/dist/types/src/types/index.d.ts.map +1 -0
  244. package/dist/types/src/types/schema.d.ts +317 -0
  245. package/dist/types/src/types/schema.d.ts.map +1 -0
  246. package/dist/types/src/types/types.d.ts +1015 -0
  247. package/dist/types/src/types/types.d.ts.map +1 -0
  248. package/dist/types/src/util.d.ts +2 -0
  249. package/dist/types/src/util.d.ts.map +1 -1
  250. package/package.json +54 -41
  251. package/src/ThreadPlugin.tsx +28 -9
  252. package/src/calls/call-manager.ts +242 -0
  253. package/src/calls/call-swarm-synchronizer.ts +350 -0
  254. package/src/calls/index.ts +10 -0
  255. package/src/calls/media-manager.ts +342 -0
  256. package/src/calls/speaking-monitor.ts +85 -0
  257. package/src/calls/types.ts +70 -0
  258. package/src/calls/util/audio.ts +58 -0
  259. package/src/calls/util/calls-service.ts +527 -0
  260. package/src/calls/util/history-cache.ts +27 -0
  261. package/src/calls/util/index.ts +8 -0
  262. package/src/calls/util/media.ts +52 -0
  263. package/src/calls/util/stub-media-streams.ts +73 -0
  264. package/src/calls/util/task-scheduling.ts +92 -0
  265. package/src/capabilities/app-graph-builder.ts +148 -12
  266. package/src/capabilities/call-manager.ts +19 -0
  267. package/src/capabilities/capabilities.ts +15 -1
  268. package/src/capabilities/index.ts +2 -0
  269. package/src/capabilities/intent-resolver.ts +69 -47
  270. package/src/capabilities/markdown.ts +51 -1
  271. package/src/capabilities/react-root.tsx +17 -0
  272. package/src/capabilities/react-surface.tsx +52 -27
  273. package/src/capabilities/state.ts +1 -1
  274. package/src/components/Call/Call.tsx +88 -0
  275. package/src/components/Call/Toolbar.tsx +182 -0
  276. package/src/components/Call/index.ts +6 -0
  277. package/src/components/CallDebugPanel.tsx +115 -0
  278. package/src/components/CallSidebar.tsx +17 -0
  279. package/src/components/ChannelContainer.stories.tsx +58 -0
  280. package/src/components/ChannelContainer.tsx +203 -0
  281. package/src/components/Chat.stories.tsx +6 -6
  282. package/src/components/ChatContainer.tsx +36 -24
  283. package/src/components/CommentContainer.tsx +31 -12
  284. package/src/components/Comments.stories.tsx +13 -11
  285. package/src/components/CommentsContainer.tsx +18 -28
  286. package/src/components/Lobby/Lobby.tsx +119 -0
  287. package/src/components/Lobby/index.ts +5 -0
  288. package/src/components/Media/AudioStream.tsx +67 -0
  289. package/src/components/Media/VideoObject.stories.tsx +52 -0
  290. package/src/components/Media/VideoObject.tsx +58 -0
  291. package/src/components/Media/index.ts +6 -0
  292. package/src/components/Participant/Participant.tsx +57 -0
  293. package/src/components/Participant/ParticipantGrid.tsx +88 -0
  294. package/src/components/Participant/index.ts +6 -0
  295. package/src/components/ResponsiveGrid/ResponsiveContainer.stories.tsx +54 -0
  296. package/src/components/ResponsiveGrid/ResponsiveContainer.tsx +31 -0
  297. package/src/components/ResponsiveGrid/ResponsiveGrid.stories.tsx +156 -0
  298. package/src/components/ResponsiveGrid/ResponsiveGrid.tsx +341 -0
  299. package/src/components/ResponsiveGrid/ResponsiveGridItem.tsx +131 -0
  300. package/src/components/ResponsiveGrid/index.ts +7 -0
  301. package/src/components/ThreadComplementary.tsx +37 -42
  302. package/src/components/index.ts +4 -0
  303. package/src/components/testing.ts +2 -1
  304. package/src/extensions/threads.ts +42 -24
  305. package/src/hooks/index.ts +2 -1
  306. package/src/hooks/useDebugMode.ts +25 -0
  307. package/src/index.ts +1 -1
  308. package/src/meta.ts +4 -3
  309. package/src/peers.stories.tsx +189 -0
  310. package/src/testing/index.ts +5 -0
  311. package/src/testing/stories.ts +50 -0
  312. package/src/translations.ts +44 -3
  313. package/src/types/index.ts +6 -0
  314. package/src/types/schema.ts +29 -0
  315. package/src/{types.ts → types/types.ts} +24 -14
  316. package/src/typings/peermetrics-webrtc-stats.d.ts +30 -0
  317. package/src/util.ts +12 -0
  318. package/dist/lib/browser/ChatContainer-H4XTHM3A.mjs +0 -138
  319. package/dist/lib/browser/ChatContainer-H4XTHM3A.mjs.map +0 -7
  320. package/dist/lib/browser/app-graph-builder-V7UUHGUI.mjs +0 -44
  321. package/dist/lib/browser/app-graph-builder-V7UUHGUI.mjs.map +0 -7
  322. package/dist/lib/browser/chunk-32EIWOAL.mjs.map +0 -7
  323. package/dist/lib/browser/chunk-EP6MRLY7.mjs.map +0 -7
  324. package/dist/lib/browser/chunk-I3JK6CBS.mjs +0 -209
  325. package/dist/lib/browser/chunk-I3JK6CBS.mjs.map +0 -7
  326. package/dist/lib/browser/chunk-OO7PQPV5.mjs +0 -93
  327. package/dist/lib/browser/chunk-OO7PQPV5.mjs.map +0 -7
  328. package/dist/lib/browser/chunk-WBJLUUOK.mjs.map +0 -7
  329. package/dist/lib/browser/chunk-YRGWE35W.mjs +0 -17
  330. package/dist/lib/browser/chunk-YRGWE35W.mjs.map +0 -7
  331. package/dist/lib/browser/intent-resolver-PDOLX5B7.mjs.map +0 -7
  332. package/dist/lib/browser/markdown-XJJVUBDO.mjs +0 -111
  333. package/dist/lib/browser/markdown-XJJVUBDO.mjs.map +0 -7
  334. package/dist/lib/browser/react-surface-7DJZKIOK.mjs +0 -436
  335. package/dist/lib/browser/react-surface-7DJZKIOK.mjs.map +0 -7
  336. package/dist/lib/browser/types.mjs +0 -10
  337. package/dist/lib/node/ChatContainer-TUISUIY7.cjs +0 -163
  338. package/dist/lib/node/ChatContainer-TUISUIY7.cjs.map +0 -7
  339. package/dist/lib/node/app-graph-builder-MMM26FEH.cjs +0 -60
  340. package/dist/lib/node/app-graph-builder-MMM26FEH.cjs.map +0 -7
  341. package/dist/lib/node/chunk-AZ275P4V.cjs.map +0 -7
  342. package/dist/lib/node/chunk-ETDNE4Z6.cjs +0 -234
  343. package/dist/lib/node/chunk-ETDNE4Z6.cjs.map +0 -7
  344. package/dist/lib/node/chunk-GRMB5VRN.cjs +0 -113
  345. package/dist/lib/node/chunk-GRMB5VRN.cjs.map +0 -7
  346. package/dist/lib/node/chunk-NO6WQR36.cjs.map +0 -7
  347. package/dist/lib/node/chunk-NQ5DTPOY.cjs.map +0 -7
  348. package/dist/lib/node/intent-resolver-MNTL32W2.cjs.map +0 -7
  349. package/dist/lib/node/markdown-25NI6Q4Y.cjs +0 -121
  350. package/dist/lib/node/markdown-25NI6Q4Y.cjs.map +0 -7
  351. package/dist/lib/node/react-surface-KGUZJVRL.cjs +0 -438
  352. package/dist/lib/node/react-surface-KGUZJVRL.cjs.map +0 -7
  353. package/dist/lib/node/types.cjs.map +0 -7
  354. package/dist/lib/node-esm/ChatContainer-G7VTTRDN.mjs +0 -139
  355. package/dist/lib/node-esm/ChatContainer-G7VTTRDN.mjs.map +0 -7
  356. package/dist/lib/node-esm/app-graph-builder-QEFJ2KA4.mjs +0 -45
  357. package/dist/lib/node-esm/app-graph-builder-QEFJ2KA4.mjs.map +0 -7
  358. package/dist/lib/node-esm/chunk-42F2DTMM.mjs.map +0 -7
  359. package/dist/lib/node-esm/chunk-5XUQJWUG.mjs +0 -19
  360. package/dist/lib/node-esm/chunk-5XUQJWUG.mjs.map +0 -7
  361. package/dist/lib/node-esm/chunk-A7RLBTTZ.mjs +0 -94
  362. package/dist/lib/node-esm/chunk-A7RLBTTZ.mjs.map +0 -7
  363. package/dist/lib/node-esm/chunk-IZWN4KAY.mjs.map +0 -7
  364. package/dist/lib/node-esm/chunk-V7HLWNAO.mjs +0 -210
  365. package/dist/lib/node-esm/chunk-V7HLWNAO.mjs.map +0 -7
  366. package/dist/lib/node-esm/chunk-WLY2HMDB.mjs.map +0 -7
  367. package/dist/lib/node-esm/intent-resolver-X2FNKFSK.mjs.map +0 -7
  368. package/dist/lib/node-esm/markdown-2ERASVEO.mjs +0 -112
  369. package/dist/lib/node-esm/markdown-2ERASVEO.mjs.map +0 -7
  370. package/dist/lib/node-esm/react-surface-DLD2UJRM.mjs +0 -437
  371. package/dist/lib/node-esm/react-surface-DLD2UJRM.mjs.map +0 -7
  372. package/dist/lib/node-esm/types.mjs +0 -11
  373. package/dist/types/src/components/types.d.ts +0 -28
  374. package/dist/types/src/components/types.d.ts.map +0 -1
  375. package/dist/types/src/events.d.ts +0 -4
  376. package/dist/types/src/events.d.ts.map +0 -1
  377. package/dist/types/src/types.d.ts +0 -276
  378. package/dist/types/src/types.d.ts.map +0 -1
  379. package/src/components/types.ts +0 -32
  380. package/src/events.ts +0 -11
  381. /package/dist/lib/browser/{types.mjs.map → ChannelContainer-LGYSQMC7.mjs.map} +0 -0
  382. /package/dist/lib/{node-esm/types.mjs.map → browser/ChatContainer-JWRI73OG.mjs.map} +0 -0
  383. /package/dist/lib/browser/{settings-K56QXNJ4.mjs.map → settings-KVBQQSO5.mjs.map} +0 -0
  384. /package/dist/lib/node/{settings-4WLSO726.cjs.map → settings-H45P5YO2.cjs.map} +0 -0
  385. /package/dist/lib/node-esm/{settings-5UA6FBMW.mjs.map → settings-IJGUOU3F.mjs.map} +0 -0
@@ -0,0 +1,92 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ const DefaultBatchSizeLimit = 64;
6
+
7
+ type Task<Value = unknown> = () => Promise<Value>;
8
+
9
+ export class FIFOScheduler {
10
+ #schedulerChain: Promise<void>;
11
+
12
+ constructor() {
13
+ this.#schedulerChain = Promise.resolve();
14
+ }
15
+
16
+ schedule<T>(task: Task<T>): Promise<T> {
17
+ return new Promise((resolve, reject) => {
18
+ this.#schedulerChain = this.#schedulerChain.then(async () => {
19
+ try {
20
+ resolve(await task());
21
+ } catch (error: any) {
22
+ reject(error);
23
+ }
24
+ });
25
+ });
26
+ }
27
+ }
28
+
29
+ // TODO(mykola): Reconcile with @dxos/async task scheduling.
30
+ export class BulkRequestDispatcher<RequestEntryParams, BulkResponse> {
31
+ #currentBatch: RequestEntryParams[];
32
+ #currentBulkResponse: Promise<BulkResponse> | null;
33
+ #batchSizeLimit: number;
34
+
35
+ constructor(batchSizeLimit: number = DefaultBatchSizeLimit) {
36
+ this.#currentBatch = [];
37
+ this.#currentBulkResponse = null;
38
+ this.#batchSizeLimit = batchSizeLimit;
39
+ }
40
+
41
+ /**
42
+ * Returns a bulk response promise.
43
+ * At the event loop iteration end, the accumulated entries will be dispatched as a bulk request.
44
+ */
45
+ doBulkRequest(
46
+ params: RequestEntryParams,
47
+ bulkRequestFunc: (bulkCopy: RequestEntryParams[]) => Promise<BulkResponse>,
48
+ ): Promise<BulkResponse> {
49
+ if (this.#currentBatch.length >= this.#batchSizeLimit) {
50
+ // If it reaches the batch size limit, we make another bulk request.
51
+ this.#currentBatch = [];
52
+ this.#currentBulkResponse = null;
53
+ }
54
+ this.#currentBatch.push(params);
55
+ if (this.#currentBulkResponse != null) {
56
+ return this.#currentBulkResponse;
57
+ }
58
+
59
+ // Save the current batch list reference in the function scope because this.#currentBatch could be reset
60
+ // if the batch limit is reached.
61
+ const batch = this.#currentBatch;
62
+ this.#currentBulkResponse = new Promise((resolve, reject) => {
63
+ // script
64
+ // |
65
+ // V
66
+ // microtasks (Promise)
67
+ // |
68
+ // V
69
+ // macrotasks (setTimeout)
70
+ //
71
+ // macrotasks are run in the event loop iteration end, so we use that moment to make the bulkRequestFunc call.
72
+ setTimeout(() => {
73
+ // When the bulk request happens, the batch list and the response is reset to start another batch.
74
+ // Coming callers will wait for a new response promise.
75
+ this.#currentBulkResponse = null;
76
+
77
+ // We cut here to make the bulk request.
78
+ const batchCopy = batch.splice(0, batch.length);
79
+ bulkRequestFunc(batchCopy)
80
+ .then((result) => {
81
+ resolve(result);
82
+ })
83
+ .catch((err) => {
84
+ reject(err);
85
+ });
86
+ }, 0);
87
+ });
88
+
89
+ // This bulk response needs to be processed to extract the the result for the entry.
90
+ return this.#currentBulkResponse;
91
+ }
92
+ }
@@ -5,25 +5,106 @@
5
5
  import { Rx } from '@effect-rx/rx-react';
6
6
  import { Option, pipe } from 'effect';
7
7
 
8
- import { Capabilities, contributes, type PluginContext } from '@dxos/app-framework';
9
- import { PLANK_COMPANION_TYPE, ATTENDABLE_PATH_SEPARATOR } from '@dxos/plugin-deck/types';
10
- import { createExtension } from '@dxos/plugin-graph';
11
- import { isLiveObject } from '@dxos/react-client/echo';
8
+ import { Capabilities, contributes, createIntent, type PluginContext } from '@dxos/app-framework';
9
+ import { getTypename, isInstanceOf } from '@dxos/echo-schema';
10
+ import { AttentionCapabilities } from '@dxos/plugin-attention';
11
+ import { PLANK_COMPANION_TYPE, ATTENDABLE_PATH_SEPARATOR, DECK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
12
+ import { createExtension, ROOT_ID, rxFromSignal } from '@dxos/plugin-graph';
13
+ import { fullyQualifiedId, isEchoObject } from '@dxos/react-client/echo';
12
14
 
13
- import { meta } from '../meta';
15
+ import { ThreadCapabilities } from './capabilities';
16
+ import { meta, THREAD_PLUGIN } from '../meta';
17
+ import { ChannelType, ThreadAction } from '../types';
18
+ import { getAnchor } from '../util';
14
19
 
15
- export default (context: PluginContext) =>
16
- contributes(Capabilities.AppGraphBuilder, [
20
+ export default (context: PluginContext) => {
21
+ const resolve = (typename: string) =>
22
+ context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {};
23
+
24
+ return contributes(Capabilities.AppGraphBuilder, [
25
+ createExtension({
26
+ id: `${THREAD_PLUGIN}/active-call`,
27
+ connector: (node) =>
28
+ Rx.make((get) =>
29
+ pipe(
30
+ get(node),
31
+ Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
32
+ Option.map((node) => {
33
+ const [call] = get(context.capabilities(ThreadCapabilities.CallManager));
34
+ return get(
35
+ rxFromSignal(() =>
36
+ call?.joined
37
+ ? [
38
+ {
39
+ id: `${node.id}${ATTENDABLE_PATH_SEPARATOR}active-call`,
40
+ type: DECK_COMPANION_TYPE,
41
+ data: null,
42
+ properties: {
43
+ label: ['call panel label', { ns: THREAD_PLUGIN }],
44
+ icon: 'ph--video-conference--regular',
45
+ position: 'hoist',
46
+ disposition: 'hidden',
47
+ },
48
+ },
49
+ ]
50
+ : [],
51
+ ),
52
+ );
53
+ }),
54
+ Option.getOrElse(() => []),
55
+ ),
56
+ ),
57
+ }),
58
+ // TODO(wittjosiah): The channel shouldn't become the companion during a call.
59
+ // Alternative: the call/meeting/thread should be a child node of the channel and that should be opened.
60
+ createExtension({
61
+ id: `${THREAD_PLUGIN}/channel-chat-companion`,
62
+ connector: (node) => {
63
+ return Rx.make((get) => {
64
+ return pipe(
65
+ get(node),
66
+ Option.flatMap((node) => (isInstanceOf(ChannelType, node.data) ? Option.some(node.data) : Option.none())),
67
+ Option.map((channel) => {
68
+ const callManager = context.getCapability(ThreadCapabilities.CallManager);
69
+ const joined = get(
70
+ rxFromSignal(() => callManager.joined && callManager.roomId === fullyQualifiedId(channel)),
71
+ );
72
+ if (!joined) {
73
+ return [];
74
+ }
75
+
76
+ return [
77
+ {
78
+ id: `${fullyQualifiedId(channel)}${ATTENDABLE_PATH_SEPARATOR}chat`,
79
+ type: PLANK_COMPANION_TYPE,
80
+ data: 'chat',
81
+ properties: {
82
+ label: ['channel companion label', { ns: THREAD_PLUGIN }],
83
+ icon: 'ph--hash--regular',
84
+ position: 'hoist',
85
+ disposition: 'hidden',
86
+ },
87
+ },
88
+ ];
89
+ }),
90
+ Option.getOrElse(() => []),
91
+ );
92
+ });
93
+ },
94
+ }),
17
95
  createExtension({
18
- id: `${meta.id}/comments`,
96
+ id: `${meta.id}/comments-companion`,
19
97
  connector: (node) =>
20
98
  Rx.make((get) =>
21
99
  pipe(
22
100
  get(node),
23
- Option.flatMap((node) =>
24
- // TODO(wittjosiah): Support comments on any object.
25
- isLiveObject(node.data) && Array.isArray(node.data.threads) ? Option.some(node) : Option.none(),
26
- ),
101
+ Option.flatMap((node) => {
102
+ if (!isEchoObject(node.data) || isInstanceOf(ChannelType, node.data)) {
103
+ return Option.none();
104
+ }
105
+ const metadata = resolve(getTypename(node.data)!);
106
+ return typeof metadata.comments === 'string' ? Option.some(node) : Option.none();
107
+ }),
27
108
  Option.map((node) => [
28
109
  {
29
110
  id: [node.id, 'comments'].join(ATTENDABLE_PATH_SEPARATOR),
@@ -41,4 +122,59 @@ export default (context: PluginContext) =>
41
122
  ),
42
123
  ),
43
124
  }),
125
+ createExtension({
126
+ id: `${meta.id}/comment-toolbar`,
127
+ actions: (node) =>
128
+ Rx.make((get) =>
129
+ pipe(
130
+ get(node),
131
+ Option.flatMap((node) => {
132
+ if (!isEchoObject(node.data) || isInstanceOf(ChannelType, node.data)) {
133
+ return Option.none();
134
+ }
135
+ const metadata = resolve(getTypename(node.data)!);
136
+ return typeof metadata.comments === 'string' ? Option.some(node.data) : Option.none();
137
+ }),
138
+ Option.map((object) => {
139
+ const selectionManager = context.getCapability(AttentionCapabilities.Selection);
140
+ const toolbar = get(context.capabilities(ThreadCapabilities.State))[0]?.state.toolbar ?? {};
141
+ const disabled = get(
142
+ rxFromSignal(() => {
143
+ const metadata = resolve(getTypename(object)!);
144
+ const selection = selectionManager.getSelection(fullyQualifiedId(object), metadata.selectionMode);
145
+ const anchor = getAnchor(selection);
146
+ const invalidSelection = !anchor;
147
+ const overlappingComment = toolbar[fullyQualifiedId(object)];
148
+ return (metadata.comments === 'anchored' && invalidSelection) || overlappingComment;
149
+ }),
150
+ );
151
+
152
+ return [
153
+ {
154
+ id: `${fullyQualifiedId(object)}/comment`,
155
+ data: () => {
156
+ const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
157
+ const metadata = resolve(getTypename(object)!);
158
+ const selection = selectionManager.getSelection(fullyQualifiedId(object));
159
+ // TODO(wittjosiah): Use presence of selection to determine if the comment should be anchored.
160
+ // Requires all components which support selection (e.g. table/kanban) to support anchored comments.
161
+ const anchor = metadata.comments === 'anchored' ? getAnchor(selection) : Date.now().toString();
162
+ const name = metadata.getAnchorLabel?.(object, anchor);
163
+ void dispatch(createIntent(ThreadAction.Create, { anchor, name, subject: object }));
164
+ },
165
+ properties: {
166
+ label: ['add comment label', { ns: meta.id }],
167
+ icon: 'ph--chat-text--regular',
168
+ disposition: 'toolbar',
169
+ disabled,
170
+ testId: 'thread.comment.add',
171
+ },
172
+ },
173
+ ];
174
+ }),
175
+ Option.getOrElse(() => []),
176
+ ),
177
+ ),
178
+ }),
44
179
  ]);
180
+ };
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { contributes, type PluginContext } from '@dxos/app-framework';
6
+ import { ClientCapabilities } from '@dxos/plugin-client';
7
+
8
+ import { ThreadCapabilities } from './capabilities';
9
+ import { CallManager } from '../calls';
10
+
11
+ export default async (context: PluginContext) => {
12
+ const client = context.getCapability(ClientCapabilities.Client);
13
+ const callManager = new CallManager(client);
14
+ await callManager.open();
15
+
16
+ return contributes(ThreadCapabilities.CallManager, callManager, () => {
17
+ void callManager.close();
18
+ });
19
+ };
@@ -5,10 +5,24 @@
5
5
  import { defineCapability } from '@dxos/app-framework';
6
6
  import { type DeepReadonly } from '@dxos/util';
7
7
 
8
+ import { type CallState, type MediaState, type CallManager } from '../calls';
8
9
  import { THREAD_PLUGIN } from '../meta';
9
- import { type ViewState, type ThreadState } from '../types';
10
+ import { type ViewState, type ThreadState, type ChannelType } from '../types';
10
11
 
11
12
  export namespace ThreadCapabilities {
13
+ export const CallManager = defineCapability<CallManager>(`${THREAD_PLUGIN}/capability/call-manager`);
14
+
15
+ // TODO(burdon): Better way to define specific extensions for meeting companions.
16
+ // TODO(burdon): This brings in deps from ../calls; how should we manage/minimize explicit type exposure to other plugins?
17
+ // TODO(wittjosiah): These callbacks could be intents once we support broadcast.
18
+ export type CallProperties = {
19
+ onJoin: (state: { channel?: ChannelType; roomId?: string }) => Promise<void>;
20
+ onLeave: (roomId?: string) => Promise<void>;
21
+ onCallStateUpdated: (callState: CallState) => Promise<void>;
22
+ onMediaStateUpdated: ([mediaState, isSpeaking]: [MediaState, boolean]) => Promise<void>;
23
+ };
24
+ export const CallExtension = defineCapability<CallProperties>(`${THREAD_PLUGIN}/capability/call-extension`);
25
+
12
26
  type GetViewState = (subjectId: string) => ViewState;
13
27
  export const State = defineCapability<{ state: DeepReadonly<ThreadState>; getViewState: GetViewState }>(
14
28
  `${THREAD_PLUGIN}/capability/state`,
@@ -5,8 +5,10 @@
5
5
  import { lazy } from '@dxos/app-framework';
6
6
 
7
7
  export const AppGraphBuilder = lazy(() => import('./app-graph-builder'));
8
+ export const CallManager = lazy(() => import('./call-manager'));
8
9
  export const IntentResolver = lazy(() => import('./intent-resolver'));
9
10
  export const Markdown = lazy(() => import('./markdown'));
11
+ export const ReactRoot = lazy(() => import('./react-root'));
10
12
  export const ReactSurface = lazy(() => import('./react-surface'));
11
13
  export const ThreadSettings = lazy(() => import('./settings'));
12
14
  export const ThreadState = lazy(() => import('./state'));
@@ -2,60 +2,70 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { batch } from '@preact/signals-core';
6
+
5
7
  import { Capabilities, contributes, createIntent, createResolver, type PluginContext } from '@dxos/app-framework';
6
- import { createQueueDxn, Ref } from '@dxos/echo-schema';
8
+ import { RelationSourceId, RelationTargetId } from '@dxos/echo-schema';
7
9
  import { invariant } from '@dxos/invariant';
8
- import { refFromDXN } from '@dxos/live-object';
9
- import { log } from '@dxos/log';
10
10
  import { ATTENDABLE_PATH_SEPARATOR, DeckAction } from '@dxos/plugin-deck/types';
11
11
  import { ObservabilityAction } from '@dxos/plugin-observability/types';
12
- import { ChannelType, ThreadType } from '@dxos/plugin-space/types';
13
- import { live, fullyQualifiedId, getSpace, makeRef } from '@dxos/react-client/echo';
14
- import { DataType } from '@dxos/schema';
12
+ import { SpaceAction } from '@dxos/plugin-space/types';
13
+ import { live, fullyQualifiedId, getSpace, Ref } from '@dxos/react-client/echo';
14
+ import { AnchoredTo, DataType } from '@dxos/schema';
15
15
 
16
16
  import { ThreadCapabilities } from './capabilities';
17
17
  import { THREAD_PLUGIN } from '../meta';
18
- import { ThreadAction } from '../types';
18
+ import { ChannelType, ThreadAction, ThreadType } from '../types';
19
19
 
20
20
  export default (context: PluginContext) =>
21
21
  contributes(Capabilities.IntentResolver, [
22
22
  createResolver({
23
23
  intent: ThreadAction.CreateChannel,
24
- resolve: ({ spaceId, name }) => ({
24
+ resolve: ({ name }) => ({
25
25
  data: {
26
26
  object: live(ChannelType, {
27
27
  name,
28
- queue: refFromDXN(createQueueDxn(spaceId)),
28
+ defaultThread: Ref.make(live(ThreadType, { messages: [], status: 'active' })),
29
+ threads: [],
29
30
  }),
30
31
  },
31
32
  }),
32
33
  }),
34
+ createResolver({
35
+ intent: ThreadAction.CreateChannelThread,
36
+ resolve: ({ channel }) => {
37
+ const thread = live(ThreadType, { messages: [], status: 'active' });
38
+ channel.threads.push(Ref.make(thread));
39
+ return {
40
+ data: {
41
+ object: thread,
42
+ },
43
+ };
44
+ },
45
+ }),
46
+ // TODO(wittjosiah): Break this up into more composable pieces.
33
47
  createResolver({
34
48
  intent: ThreadAction.Create,
35
- resolve: ({ name, cursor, subject }) => {
36
- // Seed the threads array if it does not exist.
37
- if (subject?.threads === undefined) {
38
- try {
39
- // Static schema will throw an error if subject does not support threads array property.
40
- subject.threads = [];
41
- } catch (err) {
42
- log.error('Subject does not support threads array', { typename: subject?.typename });
43
- return;
44
- }
45
- }
49
+ resolve: ({ name, anchor: _anchor, subject }) => {
50
+ const space = getSpace(subject);
51
+ invariant(space, 'Space not found');
46
52
 
47
53
  const { state } = context.getCapability(ThreadCapabilities.MutableState);
48
54
  const subjectId = fullyQualifiedId(subject);
49
- const thread = live(ThreadType, { name, anchor: cursor, messages: [], status: 'staged' });
55
+ const thread = live(ThreadType, { name, messages: [], status: 'staged' });
56
+ const anchor = live(AnchoredTo, {
57
+ [RelationSourceId]: thread,
58
+ [RelationTargetId]: subject,
59
+ anchor: _anchor,
60
+ });
50
61
  const draft = state.drafts[subjectId];
51
62
  if (draft) {
52
- draft.push(thread);
63
+ draft.push(anchor);
53
64
  } else {
54
- state.drafts[subjectId] = [thread];
65
+ state.drafts[subjectId] = [anchor];
55
66
  }
56
67
 
57
68
  return {
58
- data: { object: thread },
59
69
  intents: [
60
70
  createIntent(ThreadAction.Select, { current: fullyQualifiedId(thread) }),
61
71
  createIntent(DeckAction.ChangeCompanion, {
@@ -101,37 +111,35 @@ export default (context: PluginContext) =>
101
111
  }),
102
112
  createResolver({
103
113
  intent: ThreadAction.Delete,
104
- resolve: ({ subject, thread }, undo) => {
114
+ resolve: ({ subject, anchor, thread: _thread }, undo) => {
115
+ const thread = _thread ?? (anchor[RelationSourceId] as ThreadType);
105
116
  const { state } = context.getCapability(ThreadCapabilities.MutableState);
106
117
  const subjectId = fullyQualifiedId(subject);
107
118
  const draft = state.drafts[subjectId];
108
119
  if (draft) {
109
120
  // Check if we're deleting a draft; if so, remove it.
110
- const index = draft.findIndex((t) => t.id === thread.id);
121
+ const index = draft.findIndex((a) => a.id === anchor.id);
111
122
  if (index !== -1) {
112
123
  draft.splice(index, 1);
113
124
  return;
114
125
  }
115
126
  }
116
127
 
117
- const space = getSpace(thread);
118
- if (!space || !subject.threads) {
128
+ const space = getSpace(anchor);
129
+ if (!space) {
119
130
  return;
120
131
  }
121
132
 
122
133
  if (!undo) {
123
- const index = subject.threads.findIndex((t: any) => t?.id === thread.id);
124
- const cursor = subject.threads[index]?.anchor;
125
- if (index !== -1) {
126
- subject.threads?.splice(index, 1);
127
- }
128
-
129
- space.db.remove(thread);
134
+ batch(() => {
135
+ space.db.remove(thread);
136
+ space.db.remove(anchor);
137
+ });
130
138
 
131
139
  return {
132
140
  undoable: {
133
141
  message: ['thread deleted label', { ns: THREAD_PLUGIN }],
134
- data: { cursor },
142
+ data: { thread, anchor },
135
143
  },
136
144
  intents: [
137
145
  createIntent(ObservabilityAction.SendEvent, {
@@ -144,9 +152,10 @@ export default (context: PluginContext) =>
144
152
  ],
145
153
  };
146
154
  } else {
147
- // TODO(wittjosiah): SDK should do this automatically.
148
- const savedThread = space.db.add(thread);
149
- subject.threads.push(savedThread);
155
+ batch(() => {
156
+ space.db.add(thread);
157
+ space.db.add(anchor);
158
+ });
150
159
 
151
160
  return {
152
161
  intents: [
@@ -164,7 +173,8 @@ export default (context: PluginContext) =>
164
173
  }),
165
174
  createResolver({
166
175
  intent: ThreadAction.AddMessage,
167
- resolve: ({ thread, subject, sender, text }) => {
176
+ resolve: ({ anchor, subject, sender, text }) => {
177
+ const thread = anchor[RelationSourceId] as ThreadType;
168
178
  const { state } = context.getCapability(ThreadCapabilities.MutableState);
169
179
  const subjectId = fullyQualifiedId(subject);
170
180
  const space = getSpace(subject);
@@ -178,13 +188,24 @@ export default (context: PluginContext) =>
178
188
  // TODO(wittjosiah): Context based on attention.
179
189
  // context: context ? makeRef(context) : undefined,
180
190
  });
181
- thread.messages.push(makeRef(message));
191
+ thread.messages.push(Ref.make(message));
182
192
 
183
- if (state.drafts[subjectId]?.find((t) => t === thread)) {
193
+ const draft = state.drafts[subjectId]?.find((a) => a.id === anchor.id);
194
+ if (draft) {
184
195
  // Move draft to document.
185
196
  thread.status = 'active';
186
- subject.threads ? subject.threads.push(makeRef(thread)) : (subject.threads = [makeRef(thread)]);
187
- state.drafts[subjectId] = state.drafts[subjectId]?.filter(({ id }) => id !== thread.id);
197
+ // TODO(wittjosiah): This causes the thread to flash as it transitions from draft to db.
198
+ state.drafts[subjectId] = state.drafts[subjectId]?.filter((a) => a.id !== anchor.id);
199
+ intents.push(createIntent(SpaceAction.AddObject, { object: thread, target: space, hidden: true }));
200
+ intents.push(
201
+ createIntent(SpaceAction.AddRelation, {
202
+ space,
203
+ schema: AnchoredTo,
204
+ source: thread,
205
+ target: subject,
206
+ fields: { anchor: draft.anchor },
207
+ }),
208
+ );
188
209
  intents.push(
189
210
  createIntent(ObservabilityAction.SendEvent, {
190
211
  name: 'threads.create',
@@ -214,7 +235,8 @@ export default (context: PluginContext) =>
214
235
  }),
215
236
  createResolver({
216
237
  intent: ThreadAction.DeleteMessage,
217
- resolve: ({ subject, thread, messageId, message, messageIndex }, undo) => {
238
+ resolve: ({ subject, anchor, messageId, message, messageIndex }, undo) => {
239
+ const thread = anchor[RelationSourceId] as ThreadType;
218
240
  const space = getSpace(subject);
219
241
  invariant(space, 'Space not found');
220
242
 
@@ -228,7 +250,7 @@ export default (context: PluginContext) =>
228
250
  if (messageIndex === 0 && thread.messages.length === 1) {
229
251
  // If the message is the only message in the thread, delete the thread.
230
252
  return {
231
- intents: [createIntent(ThreadAction.Delete, { subject, thread })],
253
+ intents: [createIntent(ThreadAction.Delete, { subject, anchor })],
232
254
  };
233
255
  } else {
234
256
  thread.messages.splice(messageIndex, 1);
@@ -255,7 +277,7 @@ export default (context: PluginContext) =>
255
277
  return;
256
278
  }
257
279
 
258
- thread.messages.splice(messageIndex, 0, makeRef(message));
280
+ thread.messages.splice(messageIndex, 0, Ref.make(message));
259
281
  return {
260
282
  intents: [
261
283
  createIntent(ObservabilityAction.SendEvent, {
@@ -2,8 +2,13 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Capabilities, contributes, type PluginContext } from '@dxos/app-framework';
5
+ import { EditorView } from '@codemirror/view';
6
+
7
+ import { Capabilities, contributes, createIntent, type PluginContext } from '@dxos/app-framework';
8
+ import { ATTENDABLE_PATH_SEPARATOR, DeckAction } from '@dxos/plugin-deck/types';
6
9
  import { MarkdownCapabilities } from '@dxos/plugin-markdown';
10
+ import { fullyQualifiedId } from '@dxos/react-client/echo';
11
+ import { commentClickedEffect, commentsState, type EditorState, overlap } from '@dxos/react-ui-editor';
7
12
 
8
13
  import { ThreadCapabilities } from './capabilities';
9
14
  import { threads } from '../extensions';
@@ -15,4 +20,49 @@ export default (context: PluginContext) =>
15
20
  const { state } = context.getCapability(ThreadCapabilities.MutableState);
16
21
  return threads(state, doc, dispatch);
17
22
  },
23
+ ({ document: doc }) => {
24
+ const { state } = context.getCapability(ThreadCapabilities.MutableState);
25
+
26
+ return EditorView.updateListener.of((update) => {
27
+ if (update.docChanged || update.selectionSet) {
28
+ state.toolbar[fullyQualifiedId(doc)] = selectionOverlapsComment(update.state);
29
+ }
30
+ });
31
+ },
32
+ ({ document: doc }) => {
33
+ const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);
34
+ const id = fullyQualifiedId(doc);
35
+
36
+ return EditorView.updateListener.of((update) => {
37
+ update.transactions.forEach((transaction) => {
38
+ transaction.effects.forEach(async (effect) => {
39
+ if (effect.is(commentClickedEffect)) {
40
+ void dispatch(
41
+ createIntent(DeckAction.ChangeCompanion, {
42
+ primary: id,
43
+ companion: `${id}${ATTENDABLE_PATH_SEPARATOR}comments`,
44
+ }),
45
+ );
46
+ }
47
+ });
48
+ });
49
+ });
50
+ },
18
51
  ]);
52
+
53
+ const selectionOverlapsComment = (state: EditorState): boolean => {
54
+ // May not be defined if thread plugin not installed.
55
+ const commentState = state.field(commentsState, false);
56
+ if (commentState === undefined) {
57
+ return false;
58
+ }
59
+
60
+ const { selection } = state;
61
+ for (const range of selection.ranges) {
62
+ if (commentState.comments.some(({ range: commentRange }) => overlap(commentRange, range))) {
63
+ return true;
64
+ }
65
+ }
66
+
67
+ return false;
68
+ };
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { Capabilities, contributes } from '@dxos/app-framework';
8
+
9
+ import { Call } from '../components';
10
+ import { THREAD_PLUGIN } from '../meta';
11
+
12
+ export default () => {
13
+ return contributes(Capabilities.ReactRoot, {
14
+ id: THREAD_PLUGIN,
15
+ root: () => <Call.Audio />,
16
+ });
17
+ };