@dxos/plugin-transcription 0.8.1 → 0.8.2-main.10c050d

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 (258) hide show
  1. package/dist/lib/browser/TranscriptContainer-PFKLKRPQ.mjs +11 -0
  2. package/dist/lib/browser/{chunk-YCZISX63.mjs → chunk-4KCUYW2F.mjs} +139 -95
  3. package/dist/lib/browser/chunk-4KCUYW2F.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-6ZR5MDK3.mjs → chunk-GOF7MJNV.mjs} +3 -3
  5. package/dist/lib/browser/{chunk-6ZR5MDK3.mjs.map → chunk-GOF7MJNV.mjs.map} +1 -1
  6. package/dist/lib/browser/{chunk-K7FOYAXO.mjs → chunk-IGEKO7ZU.mjs} +2 -2
  7. package/dist/lib/browser/chunk-NODIXNQP.mjs +54 -0
  8. package/dist/lib/browser/chunk-NODIXNQP.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-TJPJBE45.mjs +764 -0
  10. package/dist/lib/browser/chunk-TJPJBE45.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-YEPT334A.mjs → chunk-W2KNYJD6.mjs} +4 -4
  12. package/dist/lib/browser/chunk-W2KNYJD6.mjs.map +7 -0
  13. package/dist/lib/browser/index.mjs +30 -216
  14. package/dist/lib/browser/index.mjs.map +4 -4
  15. package/dist/lib/browser/{intent-resolver-4R66ZCKT.mjs → intent-resolver-I2LJSQDR.mjs} +7 -11
  16. package/dist/lib/browser/intent-resolver-I2LJSQDR.mjs.map +7 -0
  17. package/dist/lib/browser/meta.json +1 -1
  18. package/dist/lib/browser/{react-surface-FQUHU5G7.mjs → react-surface-ID2MLJHZ.mjs} +11 -9
  19. package/dist/lib/browser/react-surface-ID2MLJHZ.mjs.map +7 -0
  20. package/dist/lib/browser/transcriber-NA7CIFIT.mjs +56 -0
  21. package/dist/lib/browser/transcriber-NA7CIFIT.mjs.map +7 -0
  22. package/dist/lib/browser/types/index.mjs +4 -8
  23. package/dist/lib/node/{TranscriptContainer-QEYTTT2V.cjs → TranscriptContainer-LHMC7553.cjs} +9 -8
  24. package/dist/lib/node/TranscriptContainer-LHMC7553.cjs.map +7 -0
  25. package/dist/lib/node/chunk-3GS5UNO5.cjs +778 -0
  26. package/dist/lib/node/chunk-3GS5UNO5.cjs.map +7 -0
  27. package/dist/lib/node/{chunk-LP4FG423.cjs → chunk-5J3FBCON.cjs} +5 -5
  28. package/dist/lib/node/{chunk-NVZWGFJL.cjs → chunk-AFUZI63A.cjs} +6 -6
  29. package/dist/lib/node/{chunk-NVZWGFJL.cjs.map → chunk-AFUZI63A.cjs.map} +1 -1
  30. package/dist/lib/node/chunk-EIMUQYCZ.cjs +74 -0
  31. package/dist/lib/node/chunk-EIMUQYCZ.cjs.map +7 -0
  32. package/dist/lib/node/{chunk-CZMLKKWT.cjs → chunk-FKDJNJ57.cjs} +8 -8
  33. package/dist/lib/node/chunk-FKDJNJ57.cjs.map +7 -0
  34. package/dist/lib/node/{chunk-FCCJALOT.cjs → chunk-ZFTXJZXH.cjs} +138 -95
  35. package/dist/lib/node/chunk-ZFTXJZXH.cjs.map +7 -0
  36. package/dist/lib/node/index.cjs +59 -251
  37. package/dist/lib/node/index.cjs.map +4 -4
  38. package/dist/lib/node/{intent-resolver-WMMCOUEH.cjs → intent-resolver-LLMGF5I5.cjs} +10 -11
  39. package/dist/lib/node/intent-resolver-LLMGF5I5.cjs.map +7 -0
  40. package/dist/lib/node/meta.json +1 -1
  41. package/dist/lib/node/{react-surface-WI2MGWCC.cjs → react-surface-SEQLEL4K.cjs} +13 -11
  42. package/dist/lib/node/react-surface-SEQLEL4K.cjs.map +7 -0
  43. package/dist/lib/node/transcriber-RKKN7LDU.cjs +68 -0
  44. package/dist/lib/node/transcriber-RKKN7LDU.cjs.map +7 -0
  45. package/dist/lib/node/types/index.cjs +7 -11
  46. package/dist/lib/node/types/index.cjs.map +2 -2
  47. package/dist/lib/node-esm/{TranscriptContainer-ZXUCOWGY.mjs → TranscriptContainer-DLNRFVTA.mjs} +4 -3
  48. package/dist/lib/node-esm/chunk-BRPFVRAD.mjs +55 -0
  49. package/dist/lib/node-esm/chunk-BRPFVRAD.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-YQYBLFNI.mjs → chunk-EFIWBQDF.mjs} +139 -95
  51. package/dist/lib/node-esm/chunk-EFIWBQDF.mjs.map +7 -0
  52. package/dist/lib/node-esm/{chunk-J4D2RBBH.mjs → chunk-KUWWHMOA.mjs} +2 -2
  53. package/dist/lib/node-esm/{chunk-PTZHDZ36.mjs → chunk-MFJPEIVB.mjs} +4 -4
  54. package/dist/lib/node-esm/chunk-MFJPEIVB.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-PR7EYODX.mjs +765 -0
  56. package/dist/lib/node-esm/chunk-PR7EYODX.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-X4DDJIVL.mjs → chunk-TXMDLLBR.mjs} +3 -3
  58. package/dist/lib/node-esm/{chunk-X4DDJIVL.mjs.map → chunk-TXMDLLBR.mjs.map} +1 -1
  59. package/dist/lib/node-esm/index.mjs +30 -216
  60. package/dist/lib/node-esm/index.mjs.map +4 -4
  61. package/dist/lib/node-esm/{intent-resolver-QN2LO5TX.mjs → intent-resolver-WOZKOEBW.mjs} +7 -11
  62. package/dist/lib/node-esm/intent-resolver-WOZKOEBW.mjs.map +7 -0
  63. package/dist/lib/node-esm/meta.json +1 -1
  64. package/dist/lib/node-esm/{react-surface-PCH6NTVB.mjs → react-surface-7MXUUTAL.mjs} +11 -9
  65. package/dist/lib/node-esm/react-surface-7MXUUTAL.mjs.map +7 -0
  66. package/dist/lib/node-esm/transcriber-5WSI6DYK.mjs +57 -0
  67. package/dist/lib/node-esm/transcriber-5WSI6DYK.mjs.map +7 -0
  68. package/dist/lib/node-esm/types/index.mjs +4 -8
  69. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  70. package/dist/types/src/capabilities/capabilities.d.ts +8 -10
  71. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  72. package/dist/types/src/capabilities/index.d.ts +2 -183
  73. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  74. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  75. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  76. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  77. package/dist/types/src/capabilities/transcriber.d.ts +2 -1
  78. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  79. package/dist/types/src/components/Transcript/Transcript.d.ts +14 -4
  80. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  81. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +19 -4
  82. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  83. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +27 -0
  84. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -0
  85. package/dist/types/src/components/Transcript/transcript-extension.d.ts +19 -0
  86. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -0
  87. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  88. package/dist/types/src/components/stories/FileTranscription.stories.d.ts +17 -0
  89. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +1 -0
  90. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +10 -0
  91. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +1 -0
  92. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +19 -0
  93. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +1 -0
  94. package/dist/types/src/components/stories/TranscriptionStory.d.ts +11 -0
  95. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +1 -0
  96. package/dist/types/src/components/stories/useIsSpeaking.d.ts +3 -0
  97. package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +1 -0
  98. package/dist/types/src/hooks/index.d.ts +1 -0
  99. package/dist/types/src/hooks/index.d.ts.map +1 -1
  100. package/dist/types/src/hooks/useAudioFile.d.ts +1 -1
  101. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  102. package/dist/types/src/hooks/useAudioTrack.d.ts +1 -1
  103. package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
  104. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +7 -0
  105. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -0
  106. package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
  107. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  108. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  109. package/dist/types/src/model/index.d.ts +2 -0
  110. package/dist/types/src/model/index.d.ts.map +1 -0
  111. package/dist/types/src/model/model.d.ts +68 -0
  112. package/dist/types/src/model/model.d.ts.map +1 -0
  113. package/dist/types/src/model/model.test.d.ts +2 -0
  114. package/dist/types/src/model/model.test.d.ts.map +1 -0
  115. package/dist/types/src/segments-normalization/index.d.ts +4 -0
  116. package/dist/types/src/segments-normalization/index.d.ts.map +1 -0
  117. package/dist/types/src/segments-normalization/message-normalizer.d.ts +26 -0
  118. package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +1 -0
  119. package/dist/types/src/segments-normalization/normalization.d.ts +520 -0
  120. package/dist/types/src/segments-normalization/normalization.d.ts.map +1 -0
  121. package/dist/types/src/segments-normalization/normalization.test.d.ts +2 -0
  122. package/dist/types/src/segments-normalization/normalization.test.d.ts.map +1 -0
  123. package/dist/types/src/segments-normalization/utils.d.ts +3 -0
  124. package/dist/types/src/segments-normalization/utils.d.ts.map +1 -0
  125. package/dist/types/src/testing/index.d.ts +2 -0
  126. package/dist/types/src/testing/index.d.ts.map +1 -0
  127. package/dist/types/src/testing/testing.d.ts +40 -0
  128. package/dist/types/src/testing/testing.d.ts.map +1 -0
  129. package/dist/types/src/transcriber/media-stream-recorder.d.ts +8 -5
  130. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  131. package/dist/types/src/transcriber/transcriber.browser.test.d.ts +2 -0
  132. package/dist/types/src/transcriber/transcriber.browser.test.d.ts.map +1 -0
  133. package/dist/types/src/transcriber/transcriber.d.ts +6 -2
  134. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  135. package/dist/types/src/transcriber/transcription-manager.d.ts +19 -15
  136. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  137. package/dist/types/src/translations.d.ts +3 -20
  138. package/dist/types/src/translations.d.ts.map +1 -1
  139. package/dist/types/src/types/schema.d.ts +23 -40
  140. package/dist/types/src/types/schema.d.ts.map +1 -1
  141. package/dist/types/src/types/types.d.ts +15 -8
  142. package/dist/types/src/types/types.d.ts.map +1 -1
  143. package/dist/types/src/util/array.d.ts.map +1 -1
  144. package/dist/types/src/util/get-time-str.d.ts.map +1 -1
  145. package/dist/types/src/util/index.d.ts +0 -1
  146. package/dist/types/src/util/index.d.ts.map +1 -1
  147. package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
  148. package/dist/types/tsconfig.tsbuildinfo +1 -1
  149. package/package.json +62 -41
  150. package/src/TranscriptionPlugin.tsx +13 -18
  151. package/src/capabilities/capabilities.ts +15 -8
  152. package/src/capabilities/index.ts +0 -2
  153. package/src/capabilities/intent-resolver.ts +7 -8
  154. package/src/capabilities/react-surface.tsx +5 -4
  155. package/src/capabilities/transcriber.ts +34 -6
  156. package/src/components/Transcript/Transcript.stories.tsx +201 -43
  157. package/src/components/Transcript/Transcript.tsx +83 -208
  158. package/src/components/Transcript/deprecated/OldTranscript.tsx +260 -0
  159. package/src/components/Transcript/transcript-extension.ts +236 -0
  160. package/src/components/TranscriptContainer.tsx +11 -6
  161. package/src/components/stories/FileTranscription.stories.tsx +240 -0
  162. package/src/components/stories/KeyWordRecognition.stories.tsx +128 -0
  163. package/src/components/stories/MicrophoneTranscription.stories.tsx +274 -0
  164. package/src/components/stories/TranscriptionStory.tsx +41 -0
  165. package/src/components/stories/useIsSpeaking.tsx +40 -0
  166. package/src/hooks/index.ts +1 -0
  167. package/src/hooks/useAudioFile.ts +39 -9
  168. package/src/hooks/useAudioTrack.ts +2 -2
  169. package/src/hooks/useQueueModelAdapter.ts +65 -0
  170. package/src/hooks/useTranscriber.ts +4 -2
  171. package/src/meta.ts +2 -2
  172. package/src/model/index.ts +5 -0
  173. package/src/model/model.test.ts +169 -0
  174. package/src/model/model.ts +229 -0
  175. package/src/segments-normalization/index.ts +7 -0
  176. package/src/segments-normalization/message-normalizer.ts +108 -0
  177. package/src/segments-normalization/normalization.test.ts +142 -0
  178. package/src/segments-normalization/normalization.ts +103 -0
  179. package/src/segments-normalization/utils.ts +9 -0
  180. package/src/testing/index.ts +5 -0
  181. package/src/testing/testing.ts +216 -0
  182. package/src/transcriber/media-stream-recorder.ts +14 -7
  183. package/src/transcriber/{transcriber.test.ts → transcriber.browser.test.ts} +7 -7
  184. package/src/transcriber/transcriber.ts +33 -20
  185. package/src/transcriber/transcription-manager.ts +87 -71
  186. package/src/translations.ts +2 -3
  187. package/src/types/schema.ts +11 -33
  188. package/src/types/types.ts +8 -12
  189. package/src/util/index.ts +0 -1
  190. package/dist/lib/browser/TranscriptContainer-JQ3HFHLQ.mjs +0 -10
  191. package/dist/lib/browser/app-graph-builder-VDQOVX7P.mjs +0 -191
  192. package/dist/lib/browser/app-graph-builder-VDQOVX7P.mjs.map +0 -7
  193. package/dist/lib/browser/chunk-G4B6CPC2.mjs +0 -253
  194. package/dist/lib/browser/chunk-G4B6CPC2.mjs.map +0 -7
  195. package/dist/lib/browser/chunk-GU2BNYKZ.mjs +0 -35
  196. package/dist/lib/browser/chunk-GU2BNYKZ.mjs.map +0 -7
  197. package/dist/lib/browser/chunk-OGNXNGJB.mjs +0 -73
  198. package/dist/lib/browser/chunk-OGNXNGJB.mjs.map +0 -7
  199. package/dist/lib/browser/chunk-YCZISX63.mjs.map +0 -7
  200. package/dist/lib/browser/chunk-YEPT334A.mjs.map +0 -7
  201. package/dist/lib/browser/intent-resolver-4R66ZCKT.mjs.map +0 -7
  202. package/dist/lib/browser/meeting-transcription-state-DLFHQK72.mjs +0 -18
  203. package/dist/lib/browser/meeting-transcription-state-DLFHQK72.mjs.map +0 -7
  204. package/dist/lib/browser/react-surface-FQUHU5G7.mjs.map +0 -7
  205. package/dist/lib/browser/transcriber-LGMIWDYI.mjs +0 -36
  206. package/dist/lib/browser/transcriber-LGMIWDYI.mjs.map +0 -7
  207. package/dist/lib/node/TranscriptContainer-QEYTTT2V.cjs.map +0 -7
  208. package/dist/lib/node/app-graph-builder-IFSIVNUR.cjs +0 -200
  209. package/dist/lib/node/app-graph-builder-IFSIVNUR.cjs.map +0 -7
  210. package/dist/lib/node/chunk-3KHUSQNW.cjs +0 -55
  211. package/dist/lib/node/chunk-3KHUSQNW.cjs.map +0 -7
  212. package/dist/lib/node/chunk-CZMLKKWT.cjs.map +0 -7
  213. package/dist/lib/node/chunk-FCCJALOT.cjs.map +0 -7
  214. package/dist/lib/node/chunk-P4VVD6ZO.cjs +0 -95
  215. package/dist/lib/node/chunk-P4VVD6ZO.cjs.map +0 -7
  216. package/dist/lib/node/chunk-UOFHT4GZ.cjs +0 -280
  217. package/dist/lib/node/chunk-UOFHT4GZ.cjs.map +0 -7
  218. package/dist/lib/node/intent-resolver-WMMCOUEH.cjs.map +0 -7
  219. package/dist/lib/node/meeting-transcription-state-URIYILEQ.cjs +0 -34
  220. package/dist/lib/node/meeting-transcription-state-URIYILEQ.cjs.map +0 -7
  221. package/dist/lib/node/react-surface-WI2MGWCC.cjs.map +0 -7
  222. package/dist/lib/node/transcriber-QG5WPKX4.cjs +0 -49
  223. package/dist/lib/node/transcriber-QG5WPKX4.cjs.map +0 -7
  224. package/dist/lib/node-esm/app-graph-builder-UL6XLIZY.mjs +0 -192
  225. package/dist/lib/node-esm/app-graph-builder-UL6XLIZY.mjs.map +0 -7
  226. package/dist/lib/node-esm/chunk-6X7X2UZF.mjs +0 -254
  227. package/dist/lib/node-esm/chunk-6X7X2UZF.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-GIO6J6FE.mjs +0 -74
  229. package/dist/lib/node-esm/chunk-GIO6J6FE.mjs.map +0 -7
  230. package/dist/lib/node-esm/chunk-HKATMD34.mjs +0 -37
  231. package/dist/lib/node-esm/chunk-HKATMD34.mjs.map +0 -7
  232. package/dist/lib/node-esm/chunk-PTZHDZ36.mjs.map +0 -7
  233. package/dist/lib/node-esm/chunk-YQYBLFNI.mjs.map +0 -7
  234. package/dist/lib/node-esm/intent-resolver-QN2LO5TX.mjs.map +0 -7
  235. package/dist/lib/node-esm/meeting-transcription-state-ZYVRAW6Y.mjs +0 -19
  236. package/dist/lib/node-esm/meeting-transcription-state-ZYVRAW6Y.mjs.map +0 -7
  237. package/dist/lib/node-esm/react-surface-PCH6NTVB.mjs.map +0 -7
  238. package/dist/lib/node-esm/transcriber-TMPYZ5L4.mjs +0 -37
  239. package/dist/lib/node-esm/transcriber-TMPYZ5L4.mjs.map +0 -7
  240. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -181
  241. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  242. package/dist/types/src/capabilities/meeting-transcription-state.d.ts +0 -6
  243. package/dist/types/src/capabilities/meeting-transcription-state.d.ts.map +0 -1
  244. package/dist/types/src/components/transcription.stories.d.ts +0 -15
  245. package/dist/types/src/components/transcription.stories.d.ts.map +0 -1
  246. package/dist/types/src/transcriber/transcriber.test.d.ts +0 -2
  247. package/dist/types/src/transcriber/transcriber.test.d.ts.map +0 -1
  248. package/dist/types/src/util/random-queue-dxn.d.ts +0 -3
  249. package/dist/types/src/util/random-queue-dxn.d.ts.map +0 -1
  250. package/src/capabilities/app-graph-builder.ts +0 -152
  251. package/src/capabilities/meeting-transcription-state.ts +0 -16
  252. package/src/components/transcription.stories.tsx +0 -196
  253. package/src/util/random-queue-dxn.ts +0 -9
  254. /package/dist/lib/browser/{TranscriptContainer-JQ3HFHLQ.mjs.map → TranscriptContainer-PFKLKRPQ.mjs.map} +0 -0
  255. /package/dist/lib/browser/{chunk-K7FOYAXO.mjs.map → chunk-IGEKO7ZU.mjs.map} +0 -0
  256. /package/dist/lib/node/{chunk-LP4FG423.cjs.map → chunk-5J3FBCON.cjs.map} +0 -0
  257. /package/dist/lib/node-esm/{TranscriptContainer-ZXUCOWGY.mjs.map → TranscriptContainer-DLNRFVTA.mjs.map} +0 -0
  258. /package/dist/lib/node-esm/{chunk-J4D2RBBH.mjs.map → chunk-KUWWHMOA.mjs.map} +0 -0
@@ -1,225 +1,100 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
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';
5
+ import React from 'react';
9
6
 
10
- import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
11
- import { useAttention } from '@dxos/react-ui-attention';
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';
12
10
  import {
13
- type DxGridCellValue,
14
- type DxGridElement,
15
- type DxGridAxisMeta,
16
- type DxGridPlaneCells,
17
- Grid,
18
- toPlaneCellIndex,
19
- } from '@dxos/react-ui-grid';
11
+ createBasicExtensions,
12
+ createMarkdownExtensions,
13
+ createRenderer,
14
+ createThemeExtensions,
15
+ decorateMarkdown,
16
+ editorWidth,
17
+ preview,
18
+ useTextEditor,
19
+ } from '@dxos/react-ui-editor';
20
20
  import { mx } from '@dxos/react-ui-theme';
21
-
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 };
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 ?? '', ''];
84
34
  }
85
- }
86
- return result;
87
- };
35
+
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
+ };
88
52
 
89
53
  export type TranscriptProps = ThemedClassName<{
90
- blocks?: TranscriptBlock[];
54
+ transcript?: TranscriptType;
55
+ space?: Space;
56
+ model: SerializationModel<DataType.Message>;
57
+ // TODO(wittjosiah): Move to container.
91
58
  attendableId?: string;
92
59
  ignoreAttention?: boolean;
93
60
  }>;
94
61
 
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]);
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]);
195
92
 
196
93
  return (
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>
94
+ <div
95
+ ref={parentRef}
96
+ className={mx('flex grow overflow-hidden', editorWidth, classNames)}
97
+ data-popover-collision-boundary={true}
98
+ />
224
99
  );
225
100
  };
@@ -0,0 +1,260 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { intervalToDuration } from 'date-fns/intervalToDuration';
6
+ import { Schema } from 'effect';
7
+ import { yieldOrContinue } from 'main-thread-scheduling';
8
+ import React, { type FC, useCallback, useEffect, useMemo, useRef, useState, type WheelEvent } from 'react';
9
+ import { type OnResizeCallback, useResizeDetector } from 'react-resize-detector';
10
+
11
+ import { Type } from '@dxos/echo';
12
+ import { IconButton, type ThemedClassName, useTranslation } from '@dxos/react-ui';
13
+ import { useAttention } from '@dxos/react-ui-attention';
14
+ import {
15
+ type DxGridCellValue,
16
+ type DxGridElement,
17
+ type DxGridAxisMeta,
18
+ type DxGridPlaneCells,
19
+ Grid,
20
+ toPlaneCellIndex,
21
+ } from '@dxos/react-ui-grid';
22
+ import { mx } from '@dxos/react-ui-theme';
23
+
24
+ import { TRANSCRIPTION_PLUGIN } from '../../../meta';
25
+
26
+ /**
27
+ * Transcription fragment.
28
+ */
29
+ const TranscriptSegment = Schema.Struct({
30
+ // TODO(burdon): TS from service is not Unix TS (x1000).
31
+ started: Schema.String,
32
+ text: Schema.String,
33
+ });
34
+
35
+ type TranscriptSegment = Schema.Schema.Type<typeof TranscriptSegment>;
36
+
37
+ /**
38
+ * Transcription block (from single speaker).
39
+ */
40
+ const TranscriptBlock = Schema.Struct({
41
+ id: Schema.String,
42
+ authorName: Schema.optional(Schema.String), // TODO(burdon): Replace with identityDid.
43
+ authorHue: Schema.optional(Schema.String), // TOOD(burdon): Remove.
44
+ segments: Schema.Array(TranscriptSegment),
45
+ }).pipe(
46
+ Type.Obj({
47
+ typename: 'dxos.org/type/TranscriptBlock',
48
+ version: '0.1.0',
49
+ }),
50
+ );
51
+
52
+ type TranscriptBlock = Schema.Schema.Type<typeof TranscriptBlock>;
53
+
54
+ // TODO(burdon): Actions (e.g., mark, summarize, translate, label, delete).
55
+
56
+ const lineHeight = 20;
57
+ const cellSpacing = 8 + 2;
58
+ const timestampColumnWidth = 68;
59
+
60
+ const authorClasses = 'font-medium text-base leading-[20px]';
61
+ const timestampClasses = 'text-xs leading-[20px] text-description pie-0 tabular-nums';
62
+ const segmentTextClasses = 'text-sm whitespace-normal hyphens-auto';
63
+ const measureClasses = mx(
64
+ // NOTE(thure): The `inline-start` value must equal `timestampColumnWidth` plus grid’s gap (1px)
65
+ 'absolute inline-start-[69px] inline-end-0 invisible z-[-1] border',
66
+ 'pli-[--dx-grid-cell-padding-inline] plb-[--dx-grid-cell-padding-block] leading-[20px]',
67
+ segmentTextClasses,
68
+ );
69
+
70
+ const rowDefault = {
71
+ grid: { size: lineHeight + cellSpacing },
72
+ };
73
+
74
+ type QueueRows = [number, number][];
75
+
76
+ const mapTranscriptQueue = (blocks?: TranscriptBlock[]): QueueRows => {
77
+ if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {
78
+ return [];
79
+ } else {
80
+ return blocks.flatMap((block, blockIndex) => {
81
+ return [
82
+ [blockIndex, -1],
83
+ ...block.segments.map((segment, segmentIndex) => {
84
+ return [blockIndex, segmentIndex] as [number, number];
85
+ }),
86
+ ];
87
+ });
88
+ }
89
+ };
90
+
91
+ const pad = (n = 0) => String(n).padStart(2, '0');
92
+
93
+ const formatTimestamp = (start: Date, end: Date) => {
94
+ const { hours, minutes, seconds } = intervalToDuration({ start, end });
95
+ return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
96
+ };
97
+
98
+ const measureRows = async (
99
+ host: HTMLDivElement,
100
+ blocks: TranscriptBlock[],
101
+ queueMap: QueueRows,
102
+ signal: AbortSignal,
103
+ ) => {
104
+ const result: DxGridAxisMeta = { grid: {} };
105
+ for (let row = 0; row < queueMap.length; row++) {
106
+ await yieldOrContinue('smooth', signal);
107
+ const [blockIndex, segmentIndex] = queueMap[row];
108
+ if (segmentIndex < 0) {
109
+ result.grid[row] = { size: lineHeight + cellSpacing };
110
+ } else {
111
+ host.textContent = blocks[blockIndex]!.segments[segmentIndex]!.text;
112
+ result.grid[row] = { size: host.offsetHeight };
113
+ }
114
+ }
115
+ return result;
116
+ };
117
+
118
+ export type TranscriptProps = ThemedClassName<{
119
+ blocks?: TranscriptBlock[];
120
+ attendableId?: string;
121
+ ignoreAttention?: boolean;
122
+ }>;
123
+
124
+ /**
125
+ * @@deprecated
126
+ */
127
+ export const Transcript: FC<TranscriptProps> = ({ classNames, blocks, attendableId, ignoreAttention }) => {
128
+ const { t } = useTranslation(TRANSCRIPTION_PLUGIN);
129
+ const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
130
+ const [rows, setRows] = useState<DxGridAxisMeta | undefined>(undefined);
131
+ const [columns, setColumns] = useState<DxGridAxisMeta | undefined>(undefined);
132
+
133
+ const queueMap = useMemo(() => mapTranscriptQueue(blocks), [blocks]);
134
+
135
+ const { hasAttention } = useAttention(attendableId);
136
+ const [autoScroll, setAutoScroll] = useState(true);
137
+ const handleWheel = useCallback(
138
+ (event: WheelEvent) => {
139
+ setAutoScroll(false);
140
+ if (!ignoreAttention && !hasAttention) {
141
+ event.stopPropagation();
142
+ }
143
+ },
144
+ [hasAttention, ignoreAttention],
145
+ );
146
+
147
+ const abortControllerRef = useRef<AbortController>();
148
+
149
+ const handleResize = useCallback(
150
+ async ({ entry }: { entry: { target: HTMLDivElement } | null }) => {
151
+ if (entry?.target && Array.isArray(blocks) && blocks[0] && queueMap) {
152
+ abortControllerRef.current?.abort();
153
+ abortControllerRef.current = new AbortController();
154
+ setColumns({ grid: { 0: { size: timestampColumnWidth }, 1: { size: entry.target.offsetWidth } } });
155
+ return measureRows(entry.target, blocks, queueMap, abortControllerRef.current.signal)
156
+ .then(setRows)
157
+ .catch(() => {
158
+ // Aborted mid-measurement by new size.
159
+ });
160
+ }
161
+ },
162
+ [blocks, queueMap],
163
+ );
164
+
165
+ const { width, ref: measureRef } = useResizeDetector({
166
+ onResize: handleResize as OnResizeCallback,
167
+ refreshOptions: { leading: true },
168
+ });
169
+
170
+ useEffect(() => {
171
+ if (queueMap.length !== Object.keys(rows?.grid ?? {}).length) {
172
+ void handleResize({ entry: { target: measureRef.current } }).then(() => {
173
+ if (autoScroll) {
174
+ // TODO(thure): Implement a deterministic way to do this when `rows` has fully settled and grid has a new `maxPosBlock`.
175
+ setTimeout(() => dxGrid?.scrollToEndRow(), 50);
176
+ }
177
+ });
178
+ }
179
+ }, [blocks, queueMap, width, autoScroll]);
180
+
181
+ useEffect(() => {
182
+ if (dxGrid && Array.isArray(blocks) && blocks[0]) {
183
+ const transcriptStart = blocks[0]!.segments[0]!.started;
184
+ dxGrid.getCells = (range, plane) => {
185
+ switch (plane) {
186
+ case 'grid': {
187
+ const cells: DxGridPlaneCells = {};
188
+ for (let row = range.start.row; row <= range.end.row && row < queueMap.length; row++) {
189
+ const [blockIndex, segmentIndex] = queueMap[row];
190
+
191
+ cells[toPlaneCellIndex({ col: 0, row })] = {
192
+ readonly: true,
193
+ value:
194
+ segmentIndex < 0
195
+ ? formatTimestamp(
196
+ new Date(transcriptStart),
197
+ new Date(blocks[blockIndex]!.segments[Math.max(0, segmentIndex)]!.started),
198
+ )
199
+ : '',
200
+ className: timestampClasses,
201
+ } satisfies DxGridCellValue;
202
+
203
+ cells[toPlaneCellIndex({ col: 1, row })] = (
204
+ segmentIndex < 0
205
+ ? {
206
+ readonly: true,
207
+ accessoryHtml: `<span data-hue="${blocks[blockIndex]!.authorHue}" class="dx-text-hue">${blocks[blockIndex]!.authorName}</span>`,
208
+ className: authorClasses,
209
+ }
210
+ : {
211
+ readonly: true,
212
+ value: blocks[blockIndex]!.segments[segmentIndex]!.text,
213
+ className: segmentTextClasses,
214
+ }
215
+ ) satisfies DxGridCellValue;
216
+ }
217
+ return cells;
218
+ }
219
+ default:
220
+ return {};
221
+ }
222
+ };
223
+ }
224
+ }, [dxGrid, blocks]);
225
+
226
+ const handleScrollToEnd = useCallback(() => {
227
+ setAutoScroll(true);
228
+ dxGrid?.scrollToEndRow();
229
+ }, [dxGrid, autoScroll]);
230
+
231
+ return (
232
+ <div role='none' className={mx('relative min-bs-0', classNames)}>
233
+ <Grid.Root id={`${attendableId}--transcript`}>
234
+ <Grid.Content
235
+ limitColumns={2}
236
+ limitRows={queueMap.length}
237
+ columns={columns}
238
+ rows={rows}
239
+ rowDefault={rowDefault}
240
+ onWheel={handleWheel}
241
+ 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'
242
+ ref={setDxGrid}
243
+ />
244
+ </Grid.Root>
245
+ <div role='none' {...{ inert: '' }} aria-hidden className={measureClasses} ref={measureRef} />
246
+ <IconButton
247
+ icon='ph--arrow-line-down--regular'
248
+ iconOnly
249
+ label={t('scroll to end label')}
250
+ tooltipSide='left'
251
+ data-state={autoScroll ? 'invisible' : 'visible'}
252
+ classNames={[
253
+ 'absolute inline-end-2 block-end-2 opacity-0 pointer-events-none',
254
+ 'data-[state="visible"]:pointer-events-auto data-[state="visible"]:opacity-100 transition-opacity',
255
+ ]}
256
+ onClick={handleScrollToEnd}
257
+ />
258
+ </div>
259
+ );
260
+ };