@dxos/plugin-transcription 0.8.2-main.fbd8ed0 → 0.8.2-staging.7ac8446

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 (251) hide show
  1. package/dist/lib/browser/{TranscriptContainer-CXA4ZBHW.mjs → TranscriptContainer-JQ3HFHLQ.mjs} +2 -3
  2. package/dist/lib/browser/app-graph-builder-VDQOVX7P.mjs +191 -0
  3. package/dist/lib/browser/app-graph-builder-VDQOVX7P.mjs.map +7 -0
  4. package/dist/lib/browser/chunk-G4B6CPC2.mjs +253 -0
  5. package/dist/lib/browser/chunk-G4B6CPC2.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-KLD7O5GJ.mjs → chunk-GU2BNYKZ.mjs} +12 -2
  7. package/dist/lib/browser/chunk-GU2BNYKZ.mjs.map +7 -0
  8. package/dist/lib/browser/chunk-K7FOYAXO.mjs +8 -0
  9. package/dist/lib/browser/chunk-K7FOYAXO.mjs.map +7 -0
  10. package/dist/lib/browser/chunk-OGNXNGJB.mjs +73 -0
  11. package/dist/lib/browser/chunk-OGNXNGJB.mjs.map +7 -0
  12. package/dist/lib/browser/{chunk-3TKXI6TQ.mjs → chunk-YCZISX63.mjs} +81 -108
  13. package/dist/lib/browser/chunk-YCZISX63.mjs.map +7 -0
  14. package/dist/lib/browser/{chunk-LTIQFX4W.mjs → chunk-YEPT334A.mjs} +1 -1
  15. package/dist/lib/browser/chunk-YEPT334A.mjs.map +7 -0
  16. package/dist/lib/browser/index.mjs +210 -47
  17. package/dist/lib/browser/index.mjs.map +4 -4
  18. package/dist/lib/browser/{intent-resolver-DZ62CJPC.mjs → intent-resolver-4R66ZCKT.mjs} +8 -8
  19. package/dist/lib/browser/intent-resolver-4R66ZCKT.mjs.map +7 -0
  20. package/dist/lib/browser/{meeting-transcription-state-MNA2XROE.mjs → meeting-transcription-state-DLFHQK72.mjs} +4 -4
  21. package/dist/lib/browser/meeting-transcription-state-DLFHQK72.mjs.map +7 -0
  22. package/dist/lib/browser/meta.json +1 -1
  23. package/dist/lib/browser/{react-surface-PUXZNAZL.mjs → react-surface-FQUHU5G7.mjs} +6 -18
  24. package/dist/lib/browser/react-surface-FQUHU5G7.mjs.map +7 -0
  25. package/dist/lib/browser/{transcriber-YWWIV6KP.mjs → transcriber-LGMIWDYI.mjs} +7 -8
  26. package/dist/lib/browser/{transcriber-YWWIV6KP.mjs.map → transcriber-LGMIWDYI.mjs.map} +3 -3
  27. package/dist/lib/browser/types/index.mjs +3 -3
  28. package/dist/lib/node/{TranscriptContainer-SSJVLRM6.cjs → TranscriptContainer-QEYTTT2V.cjs} +7 -8
  29. package/dist/lib/node/TranscriptContainer-QEYTTT2V.cjs.map +7 -0
  30. package/dist/lib/node/app-graph-builder-IFSIVNUR.cjs +200 -0
  31. package/dist/lib/node/app-graph-builder-IFSIVNUR.cjs.map +7 -0
  32. package/dist/lib/node/{chunk-J72PUQ3C.cjs → chunk-3KHUSQNW.cjs} +15 -6
  33. package/dist/lib/node/chunk-3KHUSQNW.cjs.map +7 -0
  34. package/dist/lib/node/{chunk-JUGOXWES.cjs → chunk-CZMLKKWT.cjs} +4 -4
  35. package/dist/lib/node/chunk-CZMLKKWT.cjs.map +7 -0
  36. package/dist/lib/node/{chunk-WKZLGVEJ.cjs → chunk-FCCJALOT.cjs} +83 -111
  37. package/dist/lib/node/chunk-FCCJALOT.cjs.map +7 -0
  38. package/dist/lib/node/{chunk-L27AK6FQ.cjs → chunk-LP4FG423.cjs} +8 -23
  39. package/dist/lib/node/chunk-LP4FG423.cjs.map +7 -0
  40. package/dist/lib/node/{chunk-EJWB53WK.cjs → chunk-P4VVD6ZO.cjs} +34 -24
  41. package/dist/lib/node/chunk-P4VVD6ZO.cjs.map +7 -0
  42. package/dist/lib/node/chunk-UOFHT4GZ.cjs +280 -0
  43. package/dist/lib/node/chunk-UOFHT4GZ.cjs.map +7 -0
  44. package/dist/lib/node/index.cjs +238 -66
  45. package/dist/lib/node/index.cjs.map +4 -4
  46. package/dist/lib/node/{intent-resolver-TRBKI24D.cjs → intent-resolver-WMMCOUEH.cjs} +10 -11
  47. package/dist/lib/node/intent-resolver-WMMCOUEH.cjs.map +7 -0
  48. package/dist/lib/node/{meeting-transcription-state-5XLTMWPA.cjs → meeting-transcription-state-URIYILEQ.cjs} +7 -7
  49. package/dist/lib/node/meeting-transcription-state-URIYILEQ.cjs.map +7 -0
  50. package/dist/lib/node/meta.json +1 -1
  51. package/dist/lib/node/{react-surface-SZAMHCX7.cjs → react-surface-WI2MGWCC.cjs} +10 -20
  52. package/dist/lib/node/react-surface-WI2MGWCC.cjs.map +7 -0
  53. package/dist/lib/node/{transcriber-AFUXFHCR.cjs → transcriber-QG5WPKX4.cjs} +13 -14
  54. package/dist/lib/node/{transcriber-AFUXFHCR.cjs.map → transcriber-QG5WPKX4.cjs.map} +3 -3
  55. package/dist/lib/node/types/index.cjs +8 -8
  56. package/dist/lib/node/types/index.cjs.map +1 -1
  57. package/dist/lib/node-esm/{TranscriptContainer-M53LI2TX.mjs → TranscriptContainer-ZXUCOWGY.mjs} +2 -3
  58. package/dist/lib/node-esm/app-graph-builder-UL6XLIZY.mjs +192 -0
  59. package/dist/lib/node-esm/app-graph-builder-UL6XLIZY.mjs.map +7 -0
  60. package/dist/lib/node-esm/chunk-6X7X2UZF.mjs +254 -0
  61. package/dist/lib/node-esm/chunk-6X7X2UZF.mjs.map +7 -0
  62. package/dist/lib/node-esm/chunk-GIO6J6FE.mjs +74 -0
  63. package/dist/lib/node-esm/chunk-GIO6J6FE.mjs.map +7 -0
  64. package/dist/lib/node-esm/{chunk-QMHMEKB3.mjs → chunk-HKATMD34.mjs} +12 -2
  65. package/dist/lib/node-esm/chunk-HKATMD34.mjs.map +7 -0
  66. package/dist/lib/node-esm/chunk-J4D2RBBH.mjs +10 -0
  67. package/dist/lib/node-esm/chunk-J4D2RBBH.mjs.map +7 -0
  68. package/dist/lib/node-esm/{chunk-5E2MR5XK.mjs → chunk-PTZHDZ36.mjs} +1 -1
  69. package/dist/lib/node-esm/chunk-PTZHDZ36.mjs.map +7 -0
  70. package/dist/lib/node-esm/{chunk-V5IWBPYO.mjs → chunk-YQYBLFNI.mjs} +81 -108
  71. package/dist/lib/node-esm/chunk-YQYBLFNI.mjs.map +7 -0
  72. package/dist/lib/node-esm/index.mjs +210 -47
  73. package/dist/lib/node-esm/index.mjs.map +4 -4
  74. package/dist/lib/node-esm/{intent-resolver-HFVAL274.mjs → intent-resolver-QN2LO5TX.mjs} +8 -8
  75. package/dist/lib/node-esm/intent-resolver-QN2LO5TX.mjs.map +7 -0
  76. package/dist/lib/node-esm/{meeting-transcription-state-3WOZ52BQ.mjs → meeting-transcription-state-ZYVRAW6Y.mjs} +4 -4
  77. package/dist/lib/node-esm/meeting-transcription-state-ZYVRAW6Y.mjs.map +7 -0
  78. package/dist/lib/node-esm/meta.json +1 -1
  79. package/dist/lib/node-esm/{react-surface-UEJELL7I.mjs → react-surface-PCH6NTVB.mjs} +6 -18
  80. package/dist/lib/node-esm/react-surface-PCH6NTVB.mjs.map +7 -0
  81. package/dist/lib/node-esm/{transcriber-7RJEFPDI.mjs → transcriber-TMPYZ5L4.mjs} +7 -8
  82. package/dist/lib/node-esm/{transcriber-7RJEFPDI.mjs.map → transcriber-TMPYZ5L4.mjs.map} +3 -3
  83. package/dist/lib/node-esm/types/index.mjs +3 -3
  84. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/app-graph-builder.d.ts +179 -2
  86. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  87. package/dist/types/src/capabilities/capabilities.d.ts +7 -5
  88. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  89. package/dist/types/src/capabilities/index.d.ts +182 -4
  90. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  91. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  92. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  93. package/dist/types/src/capabilities/meeting-transcription-state.d.ts +4 -2
  94. package/dist/types/src/capabilities/meeting-transcription-state.d.ts.map +1 -1
  95. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  96. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  97. package/dist/types/src/components/Transcript/Transcript.d.ts +4 -14
  98. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  99. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +4 -19
  100. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  101. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  102. package/dist/types/src/components/index.d.ts +0 -1
  103. package/dist/types/src/components/index.d.ts.map +1 -1
  104. package/dist/types/src/components/transcription.stories.d.ts +3 -6
  105. package/dist/types/src/components/transcription.stories.d.ts.map +1 -1
  106. package/dist/types/src/hooks/index.d.ts +0 -1
  107. package/dist/types/src/hooks/index.d.ts.map +1 -1
  108. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  109. package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
  110. package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
  111. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  112. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  113. package/dist/types/src/transcriber/transcriber.d.ts +2 -2
  114. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  115. package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
  116. package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
  117. package/dist/types/src/transcriber/transcription-manager.d.ts +15 -17
  118. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  119. package/dist/types/src/translations.d.ts +18 -3
  120. package/dist/types/src/translations.d.ts.map +1 -1
  121. package/dist/types/src/types/schema.d.ts +40 -26
  122. package/dist/types/src/types/schema.d.ts.map +1 -1
  123. package/dist/types/src/types/types.d.ts +7 -18
  124. package/dist/types/src/types/types.d.ts.map +1 -1
  125. package/dist/types/src/util/array.d.ts.map +1 -1
  126. package/dist/types/src/util/get-time-str.d.ts.map +1 -1
  127. package/dist/types/src/util/index.d.ts +1 -0
  128. package/dist/types/src/util/index.d.ts.map +1 -1
  129. package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
  130. package/dist/types/src/util/random-queue-dxn.d.ts +3 -0
  131. package/dist/types/src/util/random-queue-dxn.d.ts.map +1 -0
  132. package/dist/types/tsconfig.tsbuildinfo +1 -1
  133. package/package.json +40 -57
  134. package/src/TranscriptionPlugin.tsx +13 -30
  135. package/src/capabilities/app-graph-builder.ts +111 -192
  136. package/src/capabilities/capabilities.ts +3 -4
  137. package/src/capabilities/index.ts +0 -1
  138. package/src/capabilities/intent-resolver.ts +7 -8
  139. package/src/capabilities/meeting-transcription-state.ts +2 -2
  140. package/src/capabilities/react-surface.tsx +4 -12
  141. package/src/capabilities/transcriber.ts +1 -2
  142. package/src/components/Transcript/Transcript.stories.tsx +43 -201
  143. package/src/components/Transcript/Transcript.tsx +208 -83
  144. package/src/components/TranscriptContainer.tsx +6 -11
  145. package/src/components/index.ts +0 -1
  146. package/src/components/transcription.stories.tsx +65 -164
  147. package/src/hooks/index.ts +0 -1
  148. package/src/hooks/useTranscriber.ts +2 -3
  149. package/src/transcriber/{transcriber.browser.test.ts → transcriber.test.ts} +3 -3
  150. package/src/transcriber/transcriber.ts +11 -20
  151. package/src/transcriber/transcription-manager.ts +58 -67
  152. package/src/translations.ts +0 -2
  153. package/src/types/schema.ts +32 -11
  154. package/src/types/types.ts +9 -19
  155. package/src/util/index.ts +1 -0
  156. package/src/util/random-queue-dxn.ts +9 -0
  157. package/dist/lib/browser/app-graph-builder-REQZ5SC5.mjs +0 -351
  158. package/dist/lib/browser/app-graph-builder-REQZ5SC5.mjs.map +0 -7
  159. package/dist/lib/browser/chunk-3TKXI6TQ.mjs.map +0 -7
  160. package/dist/lib/browser/chunk-JHWEZRWC.mjs +0 -63
  161. package/dist/lib/browser/chunk-JHWEZRWC.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-K2LS2QOM.mjs +0 -720
  163. package/dist/lib/browser/chunk-K2LS2QOM.mjs.map +0 -7
  164. package/dist/lib/browser/chunk-KLD7O5GJ.mjs.map +0 -7
  165. package/dist/lib/browser/chunk-LCTMCTYY.mjs +0 -27
  166. package/dist/lib/browser/chunk-LCTMCTYY.mjs.map +0 -7
  167. package/dist/lib/browser/chunk-LTIQFX4W.mjs.map +0 -7
  168. package/dist/lib/browser/intent-resolver-DZ62CJPC.mjs.map +0 -7
  169. package/dist/lib/browser/meeting-transcription-state-MNA2XROE.mjs.map +0 -7
  170. package/dist/lib/browser/react-surface-PUXZNAZL.mjs.map +0 -7
  171. package/dist/lib/browser/settings-JWDS4J3F.mjs +0 -24
  172. package/dist/lib/browser/settings-JWDS4J3F.mjs.map +0 -7
  173. package/dist/lib/node/TranscriptContainer-SSJVLRM6.cjs.map +0 -7
  174. package/dist/lib/node/app-graph-builder-OAPU4GDH.cjs +0 -352
  175. package/dist/lib/node/app-graph-builder-OAPU4GDH.cjs.map +0 -7
  176. package/dist/lib/node/chunk-EJWB53WK.cjs.map +0 -7
  177. package/dist/lib/node/chunk-EVE3H46F.cjs +0 -737
  178. package/dist/lib/node/chunk-EVE3H46F.cjs.map +0 -7
  179. package/dist/lib/node/chunk-J72PUQ3C.cjs.map +0 -7
  180. package/dist/lib/node/chunk-JUGOXWES.cjs.map +0 -7
  181. package/dist/lib/node/chunk-L27AK6FQ.cjs.map +0 -7
  182. package/dist/lib/node/chunk-WKZLGVEJ.cjs.map +0 -7
  183. package/dist/lib/node/intent-resolver-TRBKI24D.cjs.map +0 -7
  184. package/dist/lib/node/meeting-transcription-state-5XLTMWPA.cjs.map +0 -7
  185. package/dist/lib/node/react-surface-SZAMHCX7.cjs.map +0 -7
  186. package/dist/lib/node/settings-AWWMOSCV.cjs +0 -38
  187. package/dist/lib/node/settings-AWWMOSCV.cjs.map +0 -7
  188. package/dist/lib/node-esm/app-graph-builder-UBP6EOBR.mjs +0 -352
  189. package/dist/lib/node-esm/app-graph-builder-UBP6EOBR.mjs.map +0 -7
  190. package/dist/lib/node-esm/chunk-4KC3E5T4.mjs +0 -721
  191. package/dist/lib/node-esm/chunk-4KC3E5T4.mjs.map +0 -7
  192. package/dist/lib/node-esm/chunk-5E2MR5XK.mjs.map +0 -7
  193. package/dist/lib/node-esm/chunk-MHQP2PAF.mjs +0 -28
  194. package/dist/lib/node-esm/chunk-MHQP2PAF.mjs.map +0 -7
  195. package/dist/lib/node-esm/chunk-QMHMEKB3.mjs.map +0 -7
  196. package/dist/lib/node-esm/chunk-QYG2FOXA.mjs +0 -64
  197. package/dist/lib/node-esm/chunk-QYG2FOXA.mjs.map +0 -7
  198. package/dist/lib/node-esm/chunk-V5IWBPYO.mjs.map +0 -7
  199. package/dist/lib/node-esm/intent-resolver-HFVAL274.mjs.map +0 -7
  200. package/dist/lib/node-esm/meeting-transcription-state-3WOZ52BQ.mjs.map +0 -7
  201. package/dist/lib/node-esm/react-surface-UEJELL7I.mjs.map +0 -7
  202. package/dist/lib/node-esm/settings-QFNFGFJ5.mjs +0 -25
  203. package/dist/lib/node-esm/settings-QFNFGFJ5.mjs.map +0 -7
  204. package/dist/types/src/capabilities/settings.d.ts +0 -4
  205. package/dist/types/src/capabilities/settings.d.ts.map +0 -1
  206. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +0 -27
  207. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +0 -1
  208. package/dist/types/src/components/Transcript/transcript-extension.d.ts +0 -19
  209. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +0 -1
  210. package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts +0 -6
  211. package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts.map +0 -1
  212. package/dist/types/src/components/TranscriptionSettings/index.d.ts +0 -2
  213. package/dist/types/src/components/TranscriptionSettings/index.d.ts.map +0 -1
  214. package/dist/types/src/entity-extraction/entity-extraction.d.ts +0 -42
  215. package/dist/types/src/entity-extraction/entity-extraction.d.ts.map +0 -1
  216. package/dist/types/src/entity-extraction/entity-extraction.test.d.ts +0 -2
  217. package/dist/types/src/entity-extraction/entity-extraction.test.d.ts.map +0 -1
  218. package/dist/types/src/entity-extraction/index.d.ts +0 -2
  219. package/dist/types/src/entity-extraction/index.d.ts.map +0 -1
  220. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +0 -7
  221. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +0 -1
  222. package/dist/types/src/model/index.d.ts +0 -2
  223. package/dist/types/src/model/index.d.ts.map +0 -1
  224. package/dist/types/src/model/model.d.ts +0 -68
  225. package/dist/types/src/model/model.d.ts.map +0 -1
  226. package/dist/types/src/model/model.test.d.ts +0 -2
  227. package/dist/types/src/model/model.test.d.ts.map +0 -1
  228. package/dist/types/src/testing/index.d.ts +0 -2
  229. package/dist/types/src/testing/index.d.ts.map +0 -1
  230. package/dist/types/src/testing/testing.d.ts +0 -40
  231. package/dist/types/src/testing/testing.d.ts.map +0 -1
  232. package/dist/types/src/transcriber/transcriber.browser.test.d.ts +0 -2
  233. package/dist/types/src/transcriber/transcriber.browser.test.d.ts.map +0 -1
  234. package/src/capabilities/settings.ts +0 -20
  235. package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
  236. package/src/components/Transcript/transcript-extension.ts +0 -236
  237. package/src/components/TranscriptionSettings/TranscriptionSettings.tsx +0 -26
  238. package/src/components/TranscriptionSettings/index.ts +0 -5
  239. package/src/entity-extraction/entity-extraction.test.ts +0 -97
  240. package/src/entity-extraction/entity-extraction.ts +0 -149
  241. package/src/entity-extraction/index.ts +0 -5
  242. package/src/entity-extraction/system-prompt.tpl +0 -38
  243. package/src/entity-extraction/types.d.ts +0 -8
  244. package/src/hooks/useQueueModelAdapter.ts +0 -63
  245. package/src/model/index.ts +0 -5
  246. package/src/model/model.test.ts +0 -169
  247. package/src/model/model.ts +0 -229
  248. package/src/testing/index.ts +0 -5
  249. package/src/testing/testing.ts +0 -216
  250. /package/dist/lib/browser/{TranscriptContainer-CXA4ZBHW.mjs.map → TranscriptContainer-JQ3HFHLQ.mjs.map} +0 -0
  251. /package/dist/lib/node-esm/{TranscriptContainer-M53LI2TX.mjs.map → TranscriptContainer-ZXUCOWGY.mjs.map} +0 -0
@@ -4,200 +4,60 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
8
- import React, { type FC, useEffect, useMemo, useState } from 'react';
7
+ import { type StoryObj, type Meta } from '@storybook/react';
8
+ import React, { useEffect, useState } from 'react';
9
9
 
10
- import { IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
11
- import { withPluginManager } from '@dxos/app-framework/testing';
12
- import { ObjectId } from '@dxos/echo-schema';
13
- import { ClientPlugin } from '@dxos/plugin-client';
14
- import { PreviewPlugin } from '@dxos/plugin-preview';
15
- import { SpacePlugin } from '@dxos/plugin-space';
16
- import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
17
- import { ThemePlugin } from '@dxos/plugin-theme';
18
10
  import { faker } from '@dxos/random';
19
- import { useMembers, useSpace } from '@dxos/react-client/echo';
20
- import { IconButton, Toolbar } from '@dxos/react-ui';
21
- import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
22
- import { defaultTx } from '@dxos/react-ui-theme';
23
- import { DataType } from '@dxos/schema';
24
- import { Testing } from '@dxos/schema/testing';
25
- import { withLayout } from '@dxos/storybook-utils';
11
+ import { hues } from '@dxos/react-ui-theme';
12
+ import { withTheme, withLayout } from '@dxos/storybook-utils';
26
13
 
27
- import { renderMarkdown, Transcript, type TranscriptProps } from './Transcript';
28
- import { useQueueModelAdapter } from '../../hooks';
29
- import { SerializationModel } from '../../model';
30
- import {
31
- MessageBuilder,
32
- TestItem,
33
- useTestTranscriptionQueue,
34
- useTestTranscriptionQueueWithEntityExtraction,
35
- } from '../../testing';
14
+ import { Transcript } from './Transcript';
36
15
  import translations from '../../translations';
37
16
 
38
- faker.seed(1);
39
-
40
- /**
41
- * Story wrapper with test controls.
42
- */
43
- const TranscriptContainer: FC<
44
- TranscriptProps & {
45
- running: boolean;
46
- onRunningChange: (running: boolean) => void;
47
- onReset?: () => void;
48
- }
49
- > = ({ space, model, running, onRunningChange, onReset }) => {
50
- return (
51
- <div className='grid grid-rows-[1fr_40px] grow divide-y divide-separator'>
52
- <Transcript space={space} model={model} />
53
- <div className='grid grid-cols-[1fr_16rem] overflow-hidden'>
54
- <div className='flex items-center'>
55
- <SyntaxHighlighter language='json' className='text-sm'>
56
- {JSON.stringify(model.toJSON())}
57
- </SyntaxHighlighter>
58
- </div>
59
- <Toolbar.Root classNames='justify-end'>
60
- <IconButton
61
- icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
62
- label={running ? 'Pause' : 'Start'}
63
- onClick={() => onRunningChange(!running)}
64
- />
65
- {onReset && <IconButton icon='ph--x--regular' label='Reset' onClick={onReset} />}
66
- </Toolbar.Root>
67
- </div>
68
- </div>
69
- );
17
+ let start = new Date(Date.now() - 24 * 60 * 60 * 10_000);
18
+ const next = () => {
19
+ start = new Date(start.getTime() + Math.random() * 10_000);
20
+ return start;
70
21
  };
71
22
 
72
- type StoryProps = { messages?: DataType.Message[] } & Pick<TranscriptProps, 'ignoreAttention' | 'attendableId'>;
73
-
74
- /**
75
- * Basic story mutates array of messages.
76
- */
77
- const BasicStory = ({ messages: initialMessages = [], ...props }: StoryProps) => {
78
- const [reset, setReset] = useState({});
79
- const builder = useMemo(() => new MessageBuilder(), []);
80
- const model = useMemo(
81
- () => new SerializationModel<DataType.Message>(renderMarkdown([]), initialMessages),
82
- [initialMessages, reset],
83
- );
84
- const [running, setRunning] = useState(true);
85
- const [currentMessage, setCurrentMessage] = useState<DataType.Message | null>(null);
86
- useEffect(() => {
87
- if (!running) {
88
- return;
89
- }
90
-
91
- if (!currentMessage) {
92
- void builder.createMessage().then((message) => {
93
- model.appendChunk(message);
94
- setCurrentMessage(message);
95
- });
96
- return;
97
- }
98
-
99
- const i = setInterval(() => {
100
- if (currentMessage?.blocks.length && currentMessage.blocks.length >= 3) {
101
- setCurrentMessage(null);
102
- clearInterval(i);
103
- return;
104
- }
105
-
106
- currentMessage.blocks.push(builder.createBlock());
107
- model.updateChunk(currentMessage);
108
- }, 3_000);
109
-
110
- return () => clearInterval(i);
111
- }, [model, currentMessage, running]);
112
-
113
- const handleReset = () => {
114
- setCurrentMessage(null);
115
- setRunning(false);
116
- setReset({});
23
+ const users = Array.from({ length: 5 }, () => ({
24
+ authorName: faker.person.fullName(),
25
+ authorHue: faker.helpers.arrayElement(hues),
26
+ }));
27
+
28
+ const createBlock = () => {
29
+ const author = faker.helpers.arrayElement(users);
30
+ return {
31
+ id: faker.string.uuid(),
32
+ ...author,
33
+ segments: Array.from({ length: 1 + Math.floor(Math.random() * 2) }, () => ({
34
+ started: next(),
35
+ text: faker.lorem.paragraph(),
36
+ })),
117
37
  };
118
-
119
- return (
120
- <TranscriptContainer
121
- model={model}
122
- running={running}
123
- onRunningChange={setRunning}
124
- onReset={handleReset}
125
- {...props}
126
- />
127
- );
128
38
  };
129
39
 
130
- /**
131
- * Queue story mutates queue with model adapter.
132
- */
133
- const QueueStory = ({
134
- messages: initialMessages = [],
135
- queueId,
136
- onReset,
137
- ...props
138
- }: StoryProps & { queueId: ObjectId; onReset: () => void }) => {
139
- const [running, setRunning] = useState(true);
140
- const space = useSpace();
141
- const members = useMembers(space?.key).map((member) => member.identity);
142
- const queue = useTestTranscriptionQueue(space, queueId, running, 2_000);
143
- const model = useQueueModelAdapter(renderMarkdown(members), queue, initialMessages);
144
-
145
- return (
146
- <TranscriptContainer
147
- space={space}
148
- model={model}
149
- running={running}
150
- onRunningChange={setRunning}
151
- onReset={onReset}
152
- {...props}
153
- />
154
- );
155
- };
156
-
157
- // TODO(burdon): Reconcile with QueueStory.
158
- const EntityExtractionQueueStory = () => {
159
- const [running, setRunning] = useState(true);
160
- const space = useSpace();
161
- const members = useMembers(space?.key).map((member) => member.identity);
162
- const queue = useTestTranscriptionQueueWithEntityExtraction(space, undefined, running, 2_000);
163
- const model = useQueueModelAdapter(renderMarkdown(members), queue, []);
164
-
165
- return <TranscriptContainer space={space} model={model} running={running} onRunningChange={setRunning} />;
166
- };
167
-
168
- /**
169
- * Wrapper remounts on refresh to reload queue.
170
- */
171
- const QueueStoryWrapper = () => {
172
- const [queueId] = useState(ObjectId.random());
173
- const [key, setKey] = useState(ObjectId.random().toString());
174
- const handleReset = () => {
175
- setKey(ObjectId.random().toString());
176
- };
177
-
178
- return <QueueStory key={key} queueId={queueId} onReset={handleReset} />;
179
- };
180
-
181
- const meta: Meta<typeof QueueStory> = {
40
+ const meta: Meta<typeof Transcript> = {
182
41
  title: 'plugins/plugin-transcription/Transcript',
42
+ component: Transcript,
43
+ render: ({ blocks: initialBlocks = [], ...args }) => {
44
+ const [blocks, setBlocks] = useState(initialBlocks);
45
+ useEffect(() => {
46
+ const i = setInterval(() => {
47
+ setBlocks((blocks) => [...blocks, createBlock()]);
48
+ }, 1_000);
49
+
50
+ return () => clearInterval(i);
51
+ }, []);
52
+
53
+ return <Transcript {...args} blocks={blocks} />;
54
+ },
183
55
  decorators: [
184
- withPluginManager({
185
- plugins: [
186
- ThemePlugin({ tx: defaultTx }),
187
- StorybookLayoutPlugin(),
188
- ClientPlugin({
189
- types: [TestItem, Testing.DocumentType, DataType.Person, DataType.Organization],
190
- onClientInitialized: async (_, client) => {
191
- await client.halo.createIdentity();
192
- },
193
- }),
194
- PreviewPlugin(),
195
- SpacePlugin(),
196
- SettingsPlugin(),
197
- IntentPlugin(),
198
- ],
56
+ withTheme,
57
+ withLayout({
58
+ tooltips: true,
59
+ fullscreen: true,
199
60
  }),
200
- withLayout({ fullscreen: true }),
201
61
  ],
202
62
  parameters: {
203
63
  translations,
@@ -206,35 +66,17 @@ const meta: Meta<typeof QueueStory> = {
206
66
 
207
67
  export default meta;
208
68
 
209
- type Story = StoryObj<typeof QueueStory>;
69
+ type Story = StoryObj<typeof Transcript>;
210
70
 
211
71
  export const Default: Story = {
212
- render: BasicStory,
213
72
  args: {
214
73
  ignoreAttention: true,
215
74
  attendableId: 'story',
216
- messages: await Promise.all(Array.from({ length: 10 }, () => MessageBuilder.singleton.createMessage())),
75
+ blocks: Array.from({ length: 10 }, createBlock),
217
76
  },
218
77
  };
219
78
 
220
79
  export const Empty: Story = {
221
- render: BasicStory,
222
- args: {
223
- ignoreAttention: true,
224
- attendableId: 'story',
225
- },
226
- };
227
-
228
- export const WithQueue: Story = {
229
- render: QueueStoryWrapper,
230
- args: {
231
- ignoreAttention: true,
232
- attendableId: 'story',
233
- },
234
- };
235
-
236
- export const WithEntityExtractionQueue: Story = {
237
- render: EntityExtractionQueueStory,
238
80
  args: {
239
81
  ignoreAttention: true,
240
82
  attendableId: 'story',
@@ -1,100 +1,225 @@
1
1
  //
2
- // Copyright 2025 DXOS.org
2
+ // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React from 'react';
5
+ import { intervalToDuration } from 'date-fns/intervalToDuration';
6
+ import { yieldOrContinue } from 'main-thread-scheduling';
7
+ import React, { type FC, useCallback, useEffect, useMemo, useRef, useState, type WheelEvent } from 'react';
8
+ import { type OnResizeCallback, useResizeDetector } from 'react-resize-detector';
6
9
 
7
- import { type Space } from '@dxos/react-client/echo';
8
- import { type Identity } from '@dxos/react-client/halo';
9
- import { IconButton, type ThemedClassName, useThemeContext } from '@dxos/react-ui';
10
+ import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
11
+ import { useAttention } from '@dxos/react-ui-attention';
10
12
  import {
11
- createBasicExtensions,
12
- createMarkdownExtensions,
13
- createRenderer,
14
- createThemeExtensions,
15
- decorateMarkdown,
16
- editorWidth,
17
- preview,
18
- useTextEditor,
19
- } from '@dxos/react-ui-editor';
13
+ type DxGridCellValue,
14
+ type DxGridElement,
15
+ type DxGridAxisMeta,
16
+ type DxGridPlaneCells,
17
+ Grid,
18
+ toPlaneCellIndex,
19
+ } from '@dxos/react-ui-grid';
20
20
  import { mx } from '@dxos/react-ui-theme';
21
- import { type DataType } from '@dxos/schema';
22
- import { isNotFalsy } from '@dxos/util';
23
-
24
- import { transcript } from './transcript-extension';
25
- import { type SerializationModel } from '../../model';
26
- import { type TranscriptType } from '../../types';
27
-
28
- export const renderMarkdown =
29
- (identities: Identity[]) =>
30
- (message: DataType.Message, index: number, debug = false): string[] => {
31
- if (message.sender.role === 'assistant') {
32
- // Start/stop block.
33
- return [message.blocks.find((block) => block.type === 'transcription')?.text ?? '', ''];
34
- }
35
21
 
36
- // TODO(burdon): Use link/reference markup for users (with popover).
37
- // TODO(burdon): Color and avatar.
38
- const identity = identities.find((identity) => identity.did === message.sender.identityDid);
39
- const name =
40
- identity?.profile?.displayName ??
41
- message.sender.contact?.target?.fullName ??
42
- message.sender.name ??
43
- message.sender.email ??
44
- message.sender.identityDid;
45
- const blocks = message.blocks.filter((block) => block.type === 'transcription');
46
- return [
47
- `###### ${name}` + (debug ? ` [${index}]:${message.id}` : ''),
48
- blocks.map((block) => block.text.trim()).join(' '),
49
- '',
50
- ];
51
- };
22
+ import { TRANSCRIPTION_PLUGIN } from '../../meta';
23
+ import { type TranscriptBlock } from '../../types';
24
+
25
+ // TODO(burdon): Actions (e.g., mark, summarize, translate, label, delete).
26
+
27
+ const lineHeight = 20;
28
+ const cellSpacing = 8 + 2;
29
+ const timestampColumnWidth = 68;
30
+
31
+ const authorClasses = 'font-medium text-base leading-[20px]';
32
+ const timestampClasses = 'text-xs leading-[20px] text-description pie-0 tabular-nums';
33
+ const segmentTextClasses = 'text-sm whitespace-normal hyphens-auto';
34
+ const measureClasses = mx(
35
+ // NOTE(thure): The `inline-start` value must equal `timestampColumnWidth` plus grid’s gap (1px)
36
+ 'absolute inline-start-[69px] inline-end-0 invisible z-[-1] border',
37
+ 'pli-[--dx-grid-cell-padding-inline] plb-[--dx-grid-cell-padding-block] leading-[20px]',
38
+ segmentTextClasses,
39
+ );
40
+
41
+ const rowDefault = {
42
+ grid: { size: lineHeight + cellSpacing },
43
+ };
44
+
45
+ type QueueRows = [number, number][];
46
+
47
+ const mapTranscriptQueue = (blocks?: TranscriptBlock[]): QueueRows => {
48
+ if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {
49
+ return [];
50
+ } else {
51
+ return blocks.flatMap((block, blockIndex) => {
52
+ return [
53
+ [blockIndex, -1],
54
+ ...block.segments.map((segment, segmentIndex) => {
55
+ return [blockIndex, segmentIndex] as [number, number];
56
+ }),
57
+ ];
58
+ });
59
+ }
60
+ };
61
+
62
+ const pad = (n = 0) => String(n).padStart(2, '0');
63
+
64
+ const formatTimestamp = (start: Date, end: Date) => {
65
+ const { hours, minutes, seconds } = intervalToDuration({ start, end });
66
+ return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
67
+ };
68
+
69
+ const measureRows = async (
70
+ host: HTMLDivElement,
71
+ blocks: TranscriptBlock[],
72
+ queueMap: QueueRows,
73
+ signal: AbortSignal,
74
+ ) => {
75
+ const result: DxGridAxisMeta = { grid: {} };
76
+ for (let row = 0; row < queueMap.length; row++) {
77
+ await yieldOrContinue('smooth', signal);
78
+ const [blockIndex, segmentIndex] = queueMap[row];
79
+ if (segmentIndex < 0) {
80
+ result.grid[row] = { size: lineHeight + cellSpacing };
81
+ } else {
82
+ host.textContent = blocks[blockIndex]!.segments[segmentIndex]!.text;
83
+ result.grid[row] = { size: host.offsetHeight };
84
+ }
85
+ }
86
+ return result;
87
+ };
52
88
 
53
89
  export type TranscriptProps = ThemedClassName<{
54
- transcript?: TranscriptType;
55
- space?: Space;
56
- model: SerializationModel<DataType.Message>;
57
- // TODO(wittjosiah): Move to container.
90
+ blocks?: TranscriptBlock[];
58
91
  attendableId?: string;
59
92
  ignoreAttention?: boolean;
60
93
  }>;
61
94
 
62
- /**
63
- * Transcript component implemented using the editor.
64
- */
65
- export const Transcript = ({
66
- classNames,
67
- transcript: object,
68
- space,
69
- model,
70
- attendableId,
71
- ignoreAttention,
72
- }: TranscriptProps) => {
73
- const { themeMode } = useThemeContext();
74
- const { parentRef } = useTextEditor(() => {
75
- return {
76
- extensions: [
77
- createBasicExtensions({ readOnly: true, lineWrapping: true }),
78
- createMarkdownExtensions({ themeMode }),
79
- createThemeExtensions({ themeMode }),
80
- decorateMarkdown(),
81
- space && preview(),
82
- transcript({
83
- model,
84
- started: object?.started ? new Date(object.started) : undefined,
85
- renderButton: createRenderer(({ onClick }) => (
86
- <IconButton icon='ph--arrow-line-down--regular' iconOnly label='Scroll to bottom' onClick={onClick} />
87
- )),
88
- }),
89
- ].filter(isNotFalsy),
90
- };
91
- }, [space, model]);
95
+ export const Transcript: FC<TranscriptProps> = ({ classNames, blocks, attendableId, ignoreAttention }) => {
96
+ const { t } = useTranslation(TRANSCRIPTION_PLUGIN);
97
+ const { hasAttention } = useAttention(attendableId);
98
+ const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
99
+ const [rows, setRows] = useState<DxGridAxisMeta | undefined>(undefined);
100
+ const [columns, setColumns] = useState<DxGridAxisMeta | undefined>(undefined);
101
+ const [autoScroll, setAutoScroll] = useState(true);
102
+
103
+ const queueMap = useMemo(() => mapTranscriptQueue(blocks), [blocks]);
104
+
105
+ const handleWheel = useCallback(
106
+ (event: WheelEvent) => {
107
+ setAutoScroll(false);
108
+ if (!ignoreAttention && !hasAttention) {
109
+ event.stopPropagation();
110
+ }
111
+ },
112
+ [hasAttention, ignoreAttention],
113
+ );
114
+
115
+ const abortControllerRef = useRef<AbortController>();
116
+
117
+ const handleResize = useCallback(
118
+ async ({ entry }: { entry: { target: HTMLDivElement } | null }) => {
119
+ if (entry?.target && Array.isArray(blocks) && blocks[0] && queueMap) {
120
+ abortControllerRef.current?.abort();
121
+ abortControllerRef.current = new AbortController();
122
+ setColumns({ grid: { 0: { size: timestampColumnWidth }, 1: { size: entry.target.offsetWidth } } });
123
+ return measureRows(entry.target, blocks, queueMap, abortControllerRef.current.signal)
124
+ .then(setRows)
125
+ .catch(() => {
126
+ // Aborted mid-measurement by new size.
127
+ });
128
+ }
129
+ },
130
+ [blocks, queueMap],
131
+ );
132
+
133
+ const { width, ref: measureRef } = useResizeDetector({
134
+ onResize: handleResize as OnResizeCallback,
135
+ refreshOptions: { leading: true },
136
+ });
137
+
138
+ useEffect(() => {
139
+ if (queueMap.length !== Object.keys(rows?.grid ?? {}).length) {
140
+ void handleResize({ entry: { target: measureRef.current } }).then(() => {
141
+ if (autoScroll) {
142
+ // TODO(thure): Implement a deterministic way to do this when `rows` has fully settled and grid has a new `maxPosBlock`.
143
+ setTimeout(() => dxGrid?.scrollToEndRow(), 50);
144
+ }
145
+ });
146
+ }
147
+ }, [blocks, queueMap, width, autoScroll]);
148
+
149
+ useEffect(() => {
150
+ if (dxGrid && Array.isArray(blocks) && blocks[0]) {
151
+ const transcriptStart = blocks[0]!.segments[0]!.started;
152
+ dxGrid.getCells = (range, plane) => {
153
+ switch (plane) {
154
+ case 'grid': {
155
+ const cells: DxGridPlaneCells = {};
156
+ for (let row = range.start.row; row <= range.end.row && row < queueMap.length; row++) {
157
+ const [blockIndex, segmentIndex] = queueMap[row];
158
+
159
+ cells[toPlaneCellIndex({ col: 0, row })] = {
160
+ readonly: true,
161
+ value:
162
+ segmentIndex < 0
163
+ ? formatTimestamp(transcriptStart, blocks[blockIndex]!.segments[Math.max(0, segmentIndex)]!.started)
164
+ : '',
165
+ className: timestampClasses,
166
+ } satisfies DxGridCellValue;
167
+
168
+ cells[toPlaneCellIndex({ col: 1, row })] = (
169
+ segmentIndex < 0
170
+ ? {
171
+ readonly: true,
172
+ accessoryHtml: `<span data-hue="${blocks[blockIndex]!.authorHue}" class="dx-text-hue">${blocks[blockIndex]!.authorName}</span>`,
173
+ className: authorClasses,
174
+ }
175
+ : {
176
+ readonly: true,
177
+ value: blocks[blockIndex]!.segments[segmentIndex]!.text,
178
+ className: segmentTextClasses,
179
+ }
180
+ ) satisfies DxGridCellValue;
181
+ }
182
+ return cells;
183
+ }
184
+ default:
185
+ return {};
186
+ }
187
+ };
188
+ }
189
+ }, [dxGrid, blocks]);
190
+
191
+ const handleScrollToEnd = useCallback(() => {
192
+ setAutoScroll(true);
193
+ dxGrid?.scrollToEndRow();
194
+ }, [dxGrid, autoScroll]);
92
195
 
93
196
  return (
94
- <div
95
- ref={parentRef}
96
- className={mx('flex grow overflow-hidden', editorWidth, classNames)}
97
- data-popover-collision-boundary={true}
98
- />
197
+ <div role='none' className={mx('relative min-bs-0', classNames)}>
198
+ <Grid.Root id={`${attendableId}--transcript`}>
199
+ <Grid.Content
200
+ limitColumns={2}
201
+ limitRows={queueMap.length}
202
+ columns={columns}
203
+ rows={rows}
204
+ rowDefault={rowDefault}
205
+ onWheel={handleWheel}
206
+ className='[--dx-grid-base:var(--dx-baseSurface)] [--dx-grid-lines:var(--dx-baseSurface)] [&_.dx-grid]:min-bs-0 [&_.dx-grid]:min-is-0 [&_.dx-grid]:select-auto'
207
+ ref={setDxGrid}
208
+ />
209
+ </Grid.Root>
210
+ <div role='none' {...{ inert: '' }} aria-hidden className={measureClasses} ref={measureRef} />
211
+ <IconButton
212
+ icon='ph--arrow-line-down--regular'
213
+ iconOnly
214
+ label={t('scroll to end label')}
215
+ tooltipSide='left'
216
+ data-state={autoScroll ? 'invisible' : 'visible'}
217
+ classNames={[
218
+ 'absolute inline-end-2 block-end-2 opacity-0 pointer-events-none',
219
+ 'data-[state="visible"]:pointer-events-auto data-[state="visible"]:opacity-100 transition-opacity',
220
+ ]}
221
+ onClick={handleScrollToEnd}
222
+ />
223
+ </div>
99
224
  );
100
225
  };
@@ -4,25 +4,20 @@
4
4
 
5
5
  import React, { type FC } from 'react';
6
6
 
7
- import { fullyQualifiedId, getSpace } from '@dxos/client/echo';
8
- import { useMembers, useQueue } from '@dxos/react-client/echo';
7
+ import { fullyQualifiedId } from '@dxos/client/echo';
8
+ import { useQueue } from '@dxos/react-client/echo';
9
9
  import { StackItem } from '@dxos/react-ui-stack';
10
- import { type DataType } from '@dxos/schema';
11
10
 
12
- import { Transcript, renderMarkdown } from './Transcript';
13
- import { useQueueModelAdapter } from '../hooks';
14
- import { type TranscriptType } from '../types';
11
+ import { Transcript } from './Transcript';
12
+ import { type TranscriptBlock, type TranscriptType } from '../types';
15
13
 
16
14
  export const TranscriptionContainer: FC<{ role: string; transcript: TranscriptType }> = ({ role, transcript }) => {
17
15
  const attendableId = fullyQualifiedId(transcript);
18
- const space = getSpace(transcript);
19
- const members = useMembers(space?.key).map((member) => member.identity);
20
- const queue = useQueue<DataType.Message>(transcript.queue.dxn, { pollInterval: 1_000 });
21
- const model = useQueueModelAdapter(renderMarkdown(members), queue);
16
+ const queue = useQueue<TranscriptBlock>(transcript.queue.dxn, { pollInterval: 1_000 });
22
17
 
23
18
  return (
24
19
  <StackItem.Content role={role} classNames='container-max-width pli-2'>
25
- <Transcript attendableId={attendableId} space={space} model={model} transcript={transcript} />
20
+ <Transcript attendableId={attendableId} blocks={queue?.items} />
26
21
  </StackItem.Content>
27
22
  );
28
23
  };
@@ -6,6 +6,5 @@ import { lazy } from 'react';
6
6
 
7
7
  export * from './Transcript';
8
8
  export * from './TranscriptContainer';
9
- export * from './TranscriptionSettings';
10
9
 
11
10
  export const TranscriptContainer = lazy(() => import('./TranscriptContainer'));