@dxos/plugin-transcription 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29

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 (331) hide show
  1. package/dist/lib/neutral/TranscriptionContainer-XNGXNGPX.mjs +35 -0
  2. package/dist/lib/neutral/TranscriptionContainer-XNGXNGPX.mjs.map +7 -0
  3. package/dist/lib/neutral/TranscriptionPlugin.mjs +34 -0
  4. package/dist/lib/neutral/TranscriptionPlugin.mjs.map +7 -0
  5. package/dist/lib/neutral/TranscriptionPlugin.node.mjs +25 -0
  6. package/dist/lib/neutral/TranscriptionPlugin.node.mjs.map +7 -0
  7. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs +15 -0
  8. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs.map +7 -0
  9. package/dist/lib/neutral/blueprints/index.mjs +8 -0
  10. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  11. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  12. package/dist/lib/neutral/chunk-GL4RS533.mjs +22 -0
  13. package/dist/lib/neutral/chunk-GL4RS533.mjs.map +7 -0
  14. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  15. package/dist/lib/neutral/chunk-KAEXRX3Y.mjs +35 -0
  16. package/dist/lib/neutral/chunk-KAEXRX3Y.mjs.map +7 -0
  17. package/dist/lib/{browser/chunk-BA64VLLR.mjs → neutral/chunk-LS3R7EQI.mjs} +129 -185
  18. package/dist/lib/neutral/chunk-LS3R7EQI.mjs.map +7 -0
  19. package/dist/lib/neutral/chunk-MLRJWAQH.mjs +179 -0
  20. package/dist/lib/neutral/chunk-MLRJWAQH.mjs.map +7 -0
  21. package/dist/lib/neutral/chunk-NNR3EYR2.mjs +124 -0
  22. package/dist/lib/neutral/chunk-NNR3EYR2.mjs.map +7 -0
  23. package/dist/lib/neutral/chunk-NQQK7UZQ.mjs +244 -0
  24. package/dist/lib/neutral/chunk-NQQK7UZQ.mjs.map +7 -0
  25. package/dist/lib/neutral/chunk-WVEZMGBT.mjs +8 -0
  26. package/dist/lib/neutral/chunk-WVEZMGBT.mjs.map +7 -0
  27. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs +30 -0
  28. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
  29. package/dist/lib/neutral/components/index.mjs +207 -0
  30. package/dist/lib/neutral/components/index.mjs.map +7 -0
  31. package/dist/lib/neutral/containers/index.mjs +9 -0
  32. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  33. package/dist/lib/neutral/create-2D6S4IMQ.mjs +21 -0
  34. package/dist/lib/neutral/create-2D6S4IMQ.mjs.map +7 -0
  35. package/dist/lib/neutral/hooks/index.mjs +17 -0
  36. package/dist/lib/neutral/index.mjs +44 -0
  37. package/dist/lib/neutral/meta.json +1 -0
  38. package/dist/lib/neutral/meta.mjs +8 -0
  39. package/dist/lib/neutral/meta.mjs.map +7 -0
  40. package/dist/lib/neutral/open-2QB6IIET.mjs +27 -0
  41. package/dist/lib/neutral/open-2QB6IIET.mjs.map +7 -0
  42. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
  43. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
  44. package/dist/lib/neutral/operations/index.mjs +8 -0
  45. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  46. package/dist/lib/neutral/plugin.mjs +16 -0
  47. package/dist/lib/neutral/plugin.mjs.map +7 -0
  48. package/dist/lib/neutral/react-surface-QF4MUMWG.mjs +26 -0
  49. package/dist/lib/neutral/react-surface-QF4MUMWG.mjs.map +7 -0
  50. package/dist/lib/neutral/summarize-CJPVKWYZ.mjs +78 -0
  51. package/dist/lib/neutral/summarize-CJPVKWYZ.mjs.map +7 -0
  52. package/dist/lib/neutral/text-content-LHSUG2D2.mjs +30 -0
  53. package/dist/lib/neutral/text-content-LHSUG2D2.mjs.map +7 -0
  54. package/dist/lib/{node-esm/transcriber-QPS55YP2.mjs → neutral/transcriber-M3R6HPTZ.mjs} +20 -18
  55. package/dist/lib/neutral/transcriber-M3R6HPTZ.mjs.map +7 -0
  56. package/dist/lib/neutral/translations.mjs +22 -0
  57. package/dist/lib/neutral/translations.mjs.map +7 -0
  58. package/dist/lib/neutral/types/index.mjs +12 -0
  59. package/dist/lib/neutral/types/index.mjs.map +7 -0
  60. package/dist/types/src/TranscriptionPlugin.d.ts +3 -1
  61. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  62. package/dist/types/src/TranscriptionPlugin.node.d.ts +4 -0
  63. package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -0
  64. package/dist/types/src/TranscriptionPlugin.test.d.ts +2 -0
  65. package/dist/types/src/TranscriptionPlugin.test.d.ts.map +1 -0
  66. package/dist/types/src/blueprints/index.d.ts +2 -0
  67. package/dist/types/src/blueprints/index.d.ts.map +1 -0
  68. package/dist/types/src/blueprints/transcription-blueprint.d.ts +4 -0
  69. package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -4
  71. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  72. package/dist/types/src/capabilities/index.d.ts +10 -5
  73. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  74. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  75. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  76. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  77. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  78. package/dist/types/src/capabilities/text-content.d.ts +8 -0
  79. package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
  80. package/dist/types/src/capabilities/transcriber.d.ts +4 -3
  81. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  82. package/dist/types/src/components/Transcription/Transcription.d.ts +18 -0
  83. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -0
  84. package/dist/types/src/components/Transcription/Transcription.stories.d.ts +40 -0
  85. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -0
  86. package/dist/types/src/components/Transcription/index.d.ts +2 -0
  87. package/dist/types/src/components/Transcription/index.d.ts.map +1 -0
  88. package/dist/types/src/components/Transcription/transcription-extension.d.ts +15 -0
  89. package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -0
  90. package/dist/types/src/components/index.d.ts +1 -3
  91. package/dist/types/src/components/index.d.ts.map +1 -1
  92. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +6 -0
  93. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +1 -0
  94. package/dist/types/src/containers/TranscriptionContainer/index.d.ts +2 -0
  95. package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +1 -0
  96. package/dist/types/src/containers/index.d.ts +3 -0
  97. package/dist/types/src/containers/index.d.ts.map +1 -0
  98. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  99. package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
  100. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +1 -1
  101. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
  102. package/dist/types/src/hooks/useTranscriber.d.ts +2 -2
  103. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  104. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  105. package/dist/types/src/index.d.ts +2 -3
  106. package/dist/types/src/index.d.ts.map +1 -1
  107. package/dist/types/src/meta.d.ts +2 -3
  108. package/dist/types/src/meta.d.ts.map +1 -1
  109. package/dist/types/src/model/model.d.ts +2 -2
  110. package/dist/types/src/model/model.d.ts.map +1 -1
  111. package/dist/types/src/{segments-normalization → normalization}/index.d.ts +1 -1
  112. package/dist/types/src/normalization/index.d.ts.map +1 -0
  113. package/dist/types/src/{segments-normalization → normalization}/message-normalizer.d.ts +5 -5
  114. package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -0
  115. package/dist/types/src/normalization/normalization.d.ts +543 -0
  116. package/dist/types/src/normalization/normalization.d.ts.map +1 -0
  117. package/dist/types/src/normalization/normalization.test.d.ts.map +1 -0
  118. package/dist/types/src/normalization/utils.d.ts +3 -0
  119. package/dist/types/src/normalization/utils.d.ts.map +1 -0
  120. package/dist/types/src/operations/create.d.ts +5 -0
  121. package/dist/types/src/operations/create.d.ts.map +1 -0
  122. package/dist/types/src/operations/index.d.ts +3 -0
  123. package/dist/types/src/operations/index.d.ts.map +1 -0
  124. package/dist/types/src/operations/open.d.ts +5 -0
  125. package/dist/types/src/operations/open.d.ts.map +1 -0
  126. package/dist/types/src/operations/summarize.d.ts +8 -0
  127. package/dist/types/src/operations/summarize.d.ts.map +1 -0
  128. package/dist/types/src/plugin.d.ts +4 -0
  129. package/dist/types/src/plugin.d.ts.map +1 -0
  130. package/dist/types/src/{components/stories → stories}/FileTranscription.stories.d.ts +3 -4
  131. package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -0
  132. package/dist/types/src/{components/stories → stories}/KeyWordRecognition.stories.d.ts +2 -3
  133. package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -0
  134. package/dist/types/src/{components/stories → stories}/MicrophoneTranscription.stories.d.ts +5 -6
  135. package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +1 -0
  136. package/dist/types/src/stories/TranscriptionStory.d.ts +23 -0
  137. package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -0
  138. package/dist/types/src/stories/useIsSpeaking.d.ts.map +1 -0
  139. package/dist/types/src/testing/testing.d.ts +10 -10
  140. package/dist/types/src/testing/testing.d.ts.map +1 -1
  141. package/dist/types/src/transcriber/index.d.ts +1 -1
  142. package/dist/types/src/transcriber/index.d.ts.map +1 -1
  143. package/dist/types/src/transcriber/media-stream-recorder.d.ts +2 -2
  144. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  145. package/dist/types/src/transcriber/transcriber.d.ts +60 -5
  146. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  147. package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
  148. package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
  149. package/dist/types/src/transcriber/transcription-manager.d.ts +9 -6
  150. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  151. package/dist/types/src/translations.d.ts +7 -8
  152. package/dist/types/src/translations.d.ts.map +1 -1
  153. package/dist/types/src/types/TranscriptOperation.d.ts +917 -0
  154. package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
  155. package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
  156. package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
  157. package/dist/types/src/types/{types.d.ts → defs.d.ts} +1 -1
  158. package/dist/types/src/types/defs.d.ts.map +1 -0
  159. package/dist/types/src/types/index.d.ts +3 -3
  160. package/dist/types/src/types/index.d.ts.map +1 -1
  161. package/dist/types/src/util/array.d.ts.map +1 -1
  162. package/dist/types/src/util/get-time-str.d.ts.map +1 -1
  163. package/dist/types/src/util/index.d.ts +1 -0
  164. package/dist/types/src/util/index.d.ts.map +1 -1
  165. package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
  166. package/dist/types/src/util/render-byline.d.ts +4 -0
  167. package/dist/types/src/util/render-byline.d.ts.map +1 -0
  168. package/dist/types/tsconfig.tsbuildinfo +1 -1
  169. package/package.json +148 -82
  170. package/src/TranscriptionPlugin.node.ts +20 -0
  171. package/src/TranscriptionPlugin.test.ts +31 -0
  172. package/src/TranscriptionPlugin.tsx +22 -66
  173. package/src/blueprints/index.ts +5 -0
  174. package/src/blueprints/transcription-blueprint.ts +29 -0
  175. package/src/capabilities/blueprint-definition.ts +12 -24
  176. package/src/capabilities/index.ts +11 -7
  177. package/src/capabilities/operation-handler.ts +16 -0
  178. package/src/capabilities/react-surface.tsx +23 -14
  179. package/src/capabilities/text-content.ts +33 -0
  180. package/src/capabilities/transcriber.ts +53 -42
  181. package/src/components/{Transcript/Transcript.stories.tsx → Transcription/Transcription.stories.tsx} +62 -59
  182. package/src/components/Transcription/Transcription.tsx +64 -0
  183. package/src/components/{Transcript → Transcription}/index.ts +1 -1
  184. package/src/components/{Transcript/transcript-extension.ts → Transcription/transcription-extension.ts} +13 -9
  185. package/src/components/index.ts +1 -6
  186. package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +35 -0
  187. package/src/containers/TranscriptionContainer/index.ts +5 -0
  188. package/src/containers/index.ts +7 -0
  189. package/src/hooks/useAudioFile.ts +50 -45
  190. package/src/hooks/useQueueModelAdapter.ts +1 -1
  191. package/src/hooks/useTranscriber.ts +12 -4
  192. package/src/hooks/useVoiceInput.ts +2 -3
  193. package/src/index.ts +4 -5
  194. package/src/meta.ts +9 -6
  195. package/src/model/model.test.ts +20 -14
  196. package/src/model/model.ts +3 -3
  197. package/src/{segments-normalization → normalization}/message-normalizer.ts +19 -18
  198. package/src/{segments-normalization → normalization}/normalization.test.ts +7 -12
  199. package/src/normalization/normalization.ts +76 -0
  200. package/src/{segments-normalization → normalization}/utils.ts +2 -2
  201. package/src/operations/create.ts +24 -0
  202. package/src/operations/index.ts +11 -0
  203. package/src/operations/open.ts +28 -0
  204. package/src/operations/summarize.ts +101 -0
  205. package/src/plugin.ts +11 -0
  206. package/src/{components/stories → stories}/FileTranscription.stories.tsx +56 -43
  207. package/src/{components/stories → stories}/KeyWordRecognition.stories.tsx +36 -36
  208. package/src/{components/stories → stories}/MicrophoneTranscription.stories.tsx +60 -81
  209. package/src/stories/TranscriptionStory.tsx +93 -0
  210. package/src/testing/testing.ts +37 -37
  211. package/src/transcriber/index.ts +1 -1
  212. package/src/transcriber/media-stream-recorder.ts +3 -3
  213. package/src/transcriber/transcriber.browser.test.ts +4 -5
  214. package/src/transcriber/transcriber.test.ts +104 -0
  215. package/src/transcriber/transcriber.ts +99 -58
  216. package/src/transcriber/transcription-manager.ts +21 -15
  217. package/src/translations.ts +7 -7
  218. package/src/types/TranscriptOperation.ts +103 -0
  219. package/src/types/TranscriptionCapabilities.ts +39 -0
  220. package/src/types/index.ts +4 -3
  221. package/src/util/index.ts +1 -0
  222. package/src/util/render-byline.ts +33 -0
  223. package/dist/lib/browser/TranscriptContainer-4RBK74JP.mjs +0 -11
  224. package/dist/lib/browser/blueprint-definition-TJABB43G.mjs +0 -142
  225. package/dist/lib/browser/blueprint-definition-TJABB43G.mjs.map +0 -7
  226. package/dist/lib/browser/chunk-3X7HP44M.mjs +0 -26
  227. package/dist/lib/browser/chunk-3X7HP44M.mjs.map +0 -7
  228. package/dist/lib/browser/chunk-AGZYQGSP.mjs +0 -8
  229. package/dist/lib/browser/chunk-AGZYQGSP.mjs.map +0 -7
  230. package/dist/lib/browser/chunk-BA64VLLR.mjs.map +0 -7
  231. package/dist/lib/browser/chunk-HXVGV7T6.mjs +0 -63
  232. package/dist/lib/browser/chunk-HXVGV7T6.mjs.map +0 -7
  233. package/dist/lib/browser/chunk-TNEQFHIU.mjs +0 -16
  234. package/dist/lib/browser/chunk-TNEQFHIU.mjs.map +0 -7
  235. package/dist/lib/browser/chunk-VVXRHJCH.mjs +0 -746
  236. package/dist/lib/browser/chunk-VVXRHJCH.mjs.map +0 -7
  237. package/dist/lib/browser/index.mjs +0 -131
  238. package/dist/lib/browser/index.mjs.map +0 -7
  239. package/dist/lib/browser/intent-resolver-T3EGLS3L.mjs +0 -25
  240. package/dist/lib/browser/intent-resolver-T3EGLS3L.mjs.map +0 -7
  241. package/dist/lib/browser/meta.json +0 -1
  242. package/dist/lib/browser/react-surface-4Y3YISAD.mjs +0 -34
  243. package/dist/lib/browser/react-surface-4Y3YISAD.mjs.map +0 -7
  244. package/dist/lib/browser/transcriber-JOOAQVYV.mjs +0 -56
  245. package/dist/lib/browser/transcriber-JOOAQVYV.mjs.map +0 -7
  246. package/dist/lib/browser/types/index.mjs +0 -12
  247. package/dist/lib/node-esm/TranscriptContainer-EGAAYVOP.mjs +0 -12
  248. package/dist/lib/node-esm/blueprint-definition-R4UF7YJZ.mjs +0 -143
  249. package/dist/lib/node-esm/blueprint-definition-R4UF7YJZ.mjs.map +0 -7
  250. package/dist/lib/node-esm/chunk-3QAIZGMW.mjs +0 -17
  251. package/dist/lib/node-esm/chunk-3QAIZGMW.mjs.map +0 -7
  252. package/dist/lib/node-esm/chunk-CF5Z6FAB.mjs +0 -10
  253. package/dist/lib/node-esm/chunk-CF5Z6FAB.mjs.map +0 -7
  254. package/dist/lib/node-esm/chunk-J5GQ3KGX.mjs +0 -487
  255. package/dist/lib/node-esm/chunk-J5GQ3KGX.mjs.map +0 -7
  256. package/dist/lib/node-esm/chunk-QARUDLHN.mjs +0 -27
  257. package/dist/lib/node-esm/chunk-QARUDLHN.mjs.map +0 -7
  258. package/dist/lib/node-esm/chunk-SFQNMFHX.mjs +0 -747
  259. package/dist/lib/node-esm/chunk-SFQNMFHX.mjs.map +0 -7
  260. package/dist/lib/node-esm/chunk-W72HVQMD.mjs +0 -64
  261. package/dist/lib/node-esm/chunk-W72HVQMD.mjs.map +0 -7
  262. package/dist/lib/node-esm/index.mjs +0 -132
  263. package/dist/lib/node-esm/index.mjs.map +0 -7
  264. package/dist/lib/node-esm/intent-resolver-VDHCUAIO.mjs +0 -26
  265. package/dist/lib/node-esm/intent-resolver-VDHCUAIO.mjs.map +0 -7
  266. package/dist/lib/node-esm/meta.json +0 -1
  267. package/dist/lib/node-esm/react-surface-ZIXDGJAN.mjs +0 -35
  268. package/dist/lib/node-esm/react-surface-ZIXDGJAN.mjs.map +0 -7
  269. package/dist/lib/node-esm/transcriber-QPS55YP2.mjs.map +0 -7
  270. package/dist/lib/node-esm/types/index.mjs +0 -13
  271. package/dist/types/src/capabilities/capabilities.d.ts +0 -17
  272. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  273. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  274. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  275. package/dist/types/src/components/Transcript/Transcript.d.ts +0 -20
  276. package/dist/types/src/components/Transcript/Transcript.d.ts.map +0 -1
  277. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +0 -39
  278. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +0 -1
  279. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +0 -28
  280. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +0 -1
  281. package/dist/types/src/components/Transcript/index.d.ts +0 -2
  282. package/dist/types/src/components/Transcript/index.d.ts.map +0 -1
  283. package/dist/types/src/components/Transcript/transcript-extension.d.ts +0 -15
  284. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +0 -1
  285. package/dist/types/src/components/TranscriptContainer.d.ts +0 -9
  286. package/dist/types/src/components/TranscriptContainer.d.ts.map +0 -1
  287. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +0 -1
  288. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +0 -1
  289. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
  290. package/dist/types/src/components/stories/TranscriptionStory.d.ts +0 -12
  291. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +0 -1
  292. package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +0 -1
  293. package/dist/types/src/functions/index.d.ts +0 -3
  294. package/dist/types/src/functions/index.d.ts.map +0 -1
  295. package/dist/types/src/functions/open.d.ts +0 -7
  296. package/dist/types/src/functions/open.d.ts.map +0 -1
  297. package/dist/types/src/functions/summarize.d.ts +0 -11
  298. package/dist/types/src/functions/summarize.d.ts.map +0 -1
  299. package/dist/types/src/segments-normalization/index.d.ts.map +0 -1
  300. package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +0 -1
  301. package/dist/types/src/segments-normalization/normalization.d.ts +0 -461
  302. package/dist/types/src/segments-normalization/normalization.d.ts.map +0 -1
  303. package/dist/types/src/segments-normalization/normalization.test.d.ts.map +0 -1
  304. package/dist/types/src/segments-normalization/utils.d.ts +0 -3
  305. package/dist/types/src/segments-normalization/utils.d.ts.map +0 -1
  306. package/dist/types/src/types/Transcript.d.ts +0 -30
  307. package/dist/types/src/types/Transcript.d.ts.map +0 -1
  308. package/dist/types/src/types/TranscriptAction.d.ts +0 -23
  309. package/dist/types/src/types/TranscriptAction.d.ts.map +0 -1
  310. package/dist/types/src/types/types.d.ts.map +0 -1
  311. package/src/capabilities/capabilities.ts +0 -33
  312. package/src/capabilities/intent-resolver.ts +0 -18
  313. package/src/components/Transcript/Transcript.tsx +0 -93
  314. package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
  315. package/src/components/TranscriptContainer.tsx +0 -36
  316. package/src/components/stories/TranscriptionStory.tsx +0 -45
  317. package/src/functions/index.ts +0 -6
  318. package/src/functions/open.ts +0 -37
  319. package/src/functions/summarize.ts +0 -97
  320. package/src/segments-normalization/normalization.ts +0 -115
  321. package/src/types/Transcript.ts +0 -44
  322. package/src/types/TranscriptAction.ts +0 -24
  323. package/dist/lib/{browser/types → neutral/blueprints}/index.mjs.map +0 -0
  324. package/dist/lib/{browser/TranscriptContainer-4RBK74JP.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  325. package/dist/lib/{node-esm/types → neutral/hooks}/index.mjs.map +0 -0
  326. package/dist/lib/{node-esm/TranscriptContainer-EGAAYVOP.mjs.map → neutral/index.mjs.map} +0 -0
  327. package/dist/types/src/{segments-normalization → normalization}/normalization.test.d.ts +0 -0
  328. package/dist/types/src/{components/stories → stories}/useIsSpeaking.d.ts +0 -0
  329. package/src/{segments-normalization → normalization}/index.ts +1 -1
  330. /package/src/{components/stories → stories}/useIsSpeaking.tsx +0 -0
  331. /package/src/types/{types.ts → defs.ts} +0 -0
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
6
  import { useEffect, useMemo, useState } from 'react';
7
7
 
8
8
  import { extractionAnthropicFunction, processTranscriptMessage } from '@dxos/assistant/extraction';
@@ -10,14 +10,14 @@ import { scheduleTaskInterval } from '@dxos/async';
10
10
  import { Filter, type Queue } from '@dxos/client/echo';
11
11
  import { Context } from '@dxos/context';
12
12
  import { type Key, Obj, Ref, Type } from '@dxos/echo';
13
- import { createQueueDXN } from '@dxos/echo-schema';
14
- import { FunctionExecutor, ServiceContainer } from '@dxos/functions';
13
+ import { createQueueDXN } from '@dxos/echo/internal';
15
14
  import { IdentityDid } from '@dxos/keys';
16
15
  import { log } from '@dxos/log';
17
- import { faker } from '@dxos/random';
16
+ import { random } from '@dxos/random';
18
17
  import { type Space, useQueue } from '@dxos/react-client/echo';
19
- import { DataType } from '@dxos/schema';
20
- import { Testing, seedTestData } from '@dxos/schema/testing';
18
+ import { TestSchema } from '@dxos/schema/testing';
19
+ import { type ContentBlock, Message, Organization, Person } from '@dxos/types';
20
+ import { seedTestData } from '@dxos/types/testing';
21
21
 
22
22
  // TODO(burdon): Reconcile with plugin-markdown. Move to @dxos/schema/testing.
23
23
  export const TestItem = Schema.Struct({
@@ -30,15 +30,15 @@ export const TestItem = Schema.Struct({
30
30
  description: 'Product description',
31
31
  }),
32
32
  }).pipe(
33
- Type.Obj({
34
- typename: 'dxos.org/type/Test',
33
+ Type.object({
34
+ typename: 'org.dxos.type.test',
35
35
  version: '0.1.0',
36
36
  }),
37
37
  );
38
38
 
39
39
  // TODO(wittjosiah): Make builder generic and reuse for all message types.
40
40
  abstract class AbstractMessageBuilder {
41
- abstract createMessage(numSegments?: number): Promise<DataType.Message>;
41
+ abstract createMessage(numSegments?: number): Promise<Message.Message>;
42
42
  }
43
43
 
44
44
  /**
@@ -49,8 +49,8 @@ export class MessageBuilder extends AbstractMessageBuilder {
49
49
 
50
50
  users = Array.from({ length: 5 }, () => ({
51
51
  identityDid: IdentityDid.random().toString(),
52
- name: faker.person.fullName(),
53
- email: faker.internet.email(),
52
+ name: random.person.fullName(),
53
+ email: random.internet.email(),
54
54
  }));
55
55
 
56
56
  start = new Date(Date.now() - 24 * 60 * 60 * 10_000);
@@ -59,22 +59,27 @@ export class MessageBuilder extends AbstractMessageBuilder {
59
59
  super();
60
60
  }
61
61
 
62
- override async createMessage(numSegments = 1): Promise<DataType.Message> {
63
- return Obj.make(DataType.Message, {
62
+ override async createMessage(numSegments = 1): Promise<Message.Message> {
63
+ return Obj.make(Message.Message, {
64
64
  created: this.next().toISOString(),
65
- sender: faker.helpers.arrayElement(this.users),
65
+ sender: random.helpers.arrayElement(this.users),
66
66
  blocks: Array.from({ length: numSegments }).map(() => this.createBlock()),
67
67
  });
68
68
  }
69
69
 
70
- createBlock(): DataType.MessageBlock.Transcript {
71
- let text = faker.lorem.paragraph();
70
+ createBlock(): ContentBlock.Transcript {
71
+ let text = random.lorem.paragraph();
72
72
  if (this._space) {
73
- const label = faker.commerce.productName();
74
- const obj = this._space.db.add(Obj.make(TestItem, { title: label, description: faker.lorem.paragraph() }));
73
+ const label = random.commerce.productName();
74
+ const obj = this._space.db.add(
75
+ Obj.make(TestItem, {
76
+ title: label,
77
+ description: random.lorem.paragraph(),
78
+ }),
79
+ );
75
80
  const dxn = Ref.make(obj).dxn.toString();
76
81
  const words = text.split(' ');
77
- words.splice(Math.floor(Math.random() * words.length), 0, `[${label}][${dxn}]`);
82
+ words.splice(Math.floor(Math.random() * words.length), 0, `[${label}](${dxn})`);
78
83
  text = words.join(' ');
79
84
  }
80
85
 
@@ -93,17 +98,9 @@ export class MessageBuilder extends AbstractMessageBuilder {
93
98
 
94
99
  // TODO(burdon): Reconcile with BlockBuilder.
95
100
  class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
96
- executor = new FunctionExecutor(
97
- new ServiceContainer().setServices({
98
- // ai: {
99
- // client: this.AiService,
100
- // },
101
- }),
102
- );
103
-
104
101
  space: Space | undefined;
105
102
  currentMessage: number = 0;
106
- transcriptMessages: DataType.Message[] = [];
103
+ transcriptMessages: Message.Message[] = [];
107
104
 
108
105
  async connect(space: Space): Promise<void> {
109
106
  this.space = space;
@@ -111,14 +108,18 @@ class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
111
108
  this.transcriptMessages = transcriptMessages;
112
109
  }
113
110
 
114
- override async createMessage(): Promise<DataType.Message> {
111
+ override async createMessage(): Promise<Message.Message> {
115
112
  if (!this.space) {
116
113
  throw new Error('Space not connected');
117
114
  }
118
115
 
119
- const { objects } = await this.space.db
116
+ const objects = await this.space.db
120
117
  .query(
121
- Filter.or(Filter.type(DataType.Person), Filter.type(DataType.Organization), Filter.type(Testing.DocumentType)),
118
+ Filter.or(
119
+ Filter.type(Person.Person),
120
+ Filter.type(Organization.Organization),
121
+ Filter.type(TestSchema.DocumentType),
122
+ ),
122
123
  )
123
124
  .run();
124
125
 
@@ -129,7 +130,6 @@ class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
129
130
 
130
131
  const { message: enhancedMessage } = await processTranscriptMessage({
131
132
  input: { message },
132
- executor: this.executor,
133
133
  function: extractionAnthropicFunction,
134
134
  });
135
135
 
@@ -142,7 +142,7 @@ type UseTestTranscriptionQueue = (
142
142
  queueId?: Key.ObjectId,
143
143
  running?: boolean,
144
144
  interval?: number,
145
- ) => Queue<DataType.Message> | undefined;
145
+ ) => Queue<Message.Message> | undefined;
146
146
 
147
147
  /**
148
148
  * Test transcriptionqueue.
@@ -155,7 +155,7 @@ export const useTestTranscriptionQueue: UseTestTranscriptionQueue = (
155
155
  ) => {
156
156
  // TODO(dmaretskyi): Use space.queues.create() instead.
157
157
  const queueDxn = useMemo(() => (space ? createQueueDXN(space.id, queueId) : undefined), [space, queueId]);
158
- const queue = useQueue<DataType.Message>(queueDxn);
158
+ const queue = useQueue<Message.Message>(queueDxn);
159
159
  const builder = useMemo(() => new MessageBuilder(space), [space]);
160
160
 
161
161
  useEffect(() => {
@@ -165,7 +165,7 @@ export const useTestTranscriptionQueue: UseTestTranscriptionQueue = (
165
165
 
166
166
  const i = setInterval(() => {
167
167
  void builder.createMessage(Math.ceil(Math.random() * 3)).then(async (message) => {
168
- await queue.append([Obj.make(DataType.Message, message)]);
168
+ await queue.append([Obj.make(Message.Message, message)]);
169
169
  });
170
170
  }, interval);
171
171
  return () => clearInterval(i);
@@ -186,7 +186,7 @@ export const useTestTranscriptionQueueWithEntityExtraction: UseTestTranscription
186
186
  ) => {
187
187
  // TODO(dmaretskyi): Use space.queues.create() instead.
188
188
  const queueDxn = useMemo(() => (space ? createQueueDXN(space.id, queueId) : undefined), [space, queueId]);
189
- const queue = useQueue<DataType.Message>(queueDxn);
189
+ const queue = useQueue<Message.Message>(queueDxn);
190
190
  const [builder] = useState(() => new EntityExtractionMessageBuilder());
191
191
 
192
192
  useEffect(() => {
@@ -203,7 +203,7 @@ export const useTestTranscriptionQueueWithEntityExtraction: UseTestTranscription
203
203
  ctx,
204
204
  async () => {
205
205
  const message = await builder.createMessage();
206
- void queue.append([Obj.make(DataType.Message, message)]);
206
+ void queue.append([Obj.make(Message.Message, message)]);
207
207
  },
208
208
  interval,
209
209
  );
@@ -2,7 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export type * from './audio-recorder';
5
+ export * from './audio-recorder';
6
6
  export * from './media-stream-recorder';
7
7
  export * from './transcriber';
8
8
  export * from './transcription-manager';
@@ -19,7 +19,7 @@ const initializeExtendableMediaRecorder = async () => {
19
19
  await register(await connect());
20
20
  };
21
21
 
22
- export type MediaStreamRecorderParams = {
22
+ export type MediaStreamRecorderProps = {
23
23
  mediaStreamTrack: MediaStreamTrack;
24
24
  config: {
25
25
  interval: number;
@@ -37,12 +37,12 @@ export class MediaStreamRecorder implements AudioRecorder {
37
37
  /**
38
38
  * Default MediaRecorder implementation do not support wav encoding.
39
39
  */
40
- private readonly _config: MediaStreamRecorderParams['config'];
40
+ private readonly _config: MediaStreamRecorderProps['config'];
41
41
  private _mediaRecorder?: IMediaRecorder = undefined;
42
42
  private _header?: Uint8Array = undefined;
43
43
  private _onChunk?: (chunk: AudioChunk) => void;
44
44
 
45
- constructor({ mediaStreamTrack, config }: MediaStreamRecorderParams) {
45
+ constructor({ mediaStreamTrack, config }: MediaStreamRecorderProps) {
46
46
  this._mediaStreamTrack = mediaStreamTrack;
47
47
  this._config = config;
48
48
  }
@@ -4,15 +4,14 @@
4
4
 
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
-
8
7
  import { describe, expect, test } from 'vitest';
9
8
  import { WaveFile } from 'wavefile';
10
9
 
11
10
  import { Trigger } from '@dxos/async';
12
11
  import { log } from '@dxos/log';
13
- import { type DataType } from '@dxos/schema';
14
12
  import { openAndClose } from '@dxos/test-utils';
15
13
  import { TRACE_PROCESSOR, trace } from '@dxos/tracing';
14
+ import { type ContentBlock } from '@dxos/types';
16
15
 
17
16
  import { type AudioChunk, type AudioRecorder, Transcriber } from '../transcriber';
18
17
  import { mergeFloat64Arrays } from '../util';
@@ -94,7 +93,7 @@ describe.skip('Transcriber', () => {
94
93
  });
95
94
 
96
95
  test.skip('transcription of audio recording', { timeout: 10_000 }, async () => {
97
- const trigger = new Trigger<DataType.MessageBlock.Transcript[]>({ autoReset: true });
96
+ const trigger = new Trigger<ContentBlock.Transcript[]>({ autoReset: true });
98
97
  const recorder = new MockAudioRecorder({
99
98
  buffer: await readFile('test.wav'),
100
99
  chunkDuration: 3_000,
@@ -128,7 +127,7 @@ describe.skip('Transcriber', () => {
128
127
  });
129
128
 
130
129
  test.skip('transcription of audio recording with overlapping chunks', { timeout: 20_000 }, async () => {
131
- const trigger = new Trigger<DataType.MessageBlock.Transcript[]>({ autoReset: true });
130
+ const trigger = new Trigger<ContentBlock.Transcript[]>({ autoReset: true });
132
131
  const recorder = new MockAudioRecorder({
133
132
  buffer: await readFile('test.wav'),
134
133
  chunkDuration: 3_000,
@@ -213,7 +212,7 @@ class MockAudioRecorder implements AudioRecorder {
213
212
 
214
213
  @trace.span()
215
214
  async start(): Promise<void> {
216
- log.info('start mock audio recorder', { wavParams: this.wav.fmt });
215
+ log.info('start mock audio recorder', { wavProps: this.wav.fmt });
217
216
  const now = Date.now();
218
217
  const samples = this.wav.getSamples();
219
218
  const sampleRate = this.wavConfig.sampleRate;
@@ -0,0 +1,104 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { describe, test } from 'vitest';
6
+
7
+ import { type AudioChunk } from './audio-recorder';
8
+ import { alignWhisperSegments, type WhisperSegment } from './transcriber';
9
+
10
+ const T0 = Date.parse('2026-01-01T00:00:00.000Z');
11
+
12
+ const chunk = (offsetMs = 0, samples = 16): AudioChunk => ({
13
+ timestamp: T0 + offsetMs,
14
+ data: new Float64Array(samples),
15
+ });
16
+
17
+ const segment = (overrides: Partial<WhisperSegment>): WhisperSegment => ({
18
+ text: '',
19
+ start: 0,
20
+ end: 0,
21
+ no_speech_prob: 0,
22
+ words: [],
23
+ ...overrides,
24
+ });
25
+
26
+ describe('alignWhisperSegments', () => {
27
+ test('returns empty when no chunks were transcribed', ({ expect }) => {
28
+ const result = alignWhisperSegments(
29
+ [segment({ text: 'x', end: 1, words: [{ word: 'x', start: 0, end: 1 }] })],
30
+ [],
31
+ 0,
32
+ );
33
+ expect(result.transcripts).toEqual([]);
34
+ expect(result.lastUsedTimestamp).toBeUndefined();
35
+ });
36
+
37
+ test('absolute-timestamps and deduplicates segments against lastUsedTimestamp', ({ expect }) => {
38
+ const segments: WhisperSegment[] = [
39
+ segment({
40
+ text: 'hello world',
41
+ start: 0,
42
+ end: 1,
43
+ words: [
44
+ { word: 'hello ', start: 0, end: 0.5 },
45
+ { word: 'world', start: 0.5, end: 1 },
46
+ ],
47
+ }),
48
+ ];
49
+
50
+ const first = alignWhisperSegments(segments, [chunk(0)], 0);
51
+ expect(first.transcripts).toHaveLength(1);
52
+ expect(first.transcripts[0]._tag).toBe('transcript');
53
+ expect(first.transcripts[0].text).toBe('hello world');
54
+ expect(new Date(first.transcripts[0].started).getTime()).toBe(T0);
55
+ expect(first.lastUsedTimestamp).toBe(T0 + 1_000);
56
+
57
+ // Same segments, same chunk start: every word's absolute start is < lastUsedTimestamp -> filtered out.
58
+ const second = alignWhisperSegments(segments, [chunk(0)], first.lastUsedTimestamp!);
59
+ expect(second.transcripts).toEqual([]);
60
+ expect(second.lastUsedTimestamp).toBeUndefined();
61
+ });
62
+
63
+ test('keeps later words when earlier words fall before lastUsedTimestamp', ({ expect }) => {
64
+ const segments: WhisperSegment[] = [
65
+ segment({
66
+ text: 'old new',
67
+ start: 0,
68
+ end: 2,
69
+ words: [
70
+ { word: 'old ', start: 0, end: 1 },
71
+ { word: 'new', start: 1.5, end: 2 },
72
+ ],
73
+ }),
74
+ ];
75
+
76
+ // First word starts at T0, second at T0+1500 — only the second survives a cursor at T0+1000.
77
+ const result = alignWhisperSegments(segments, [chunk(0)], T0 + 1_000);
78
+ expect(result.transcripts).toHaveLength(1);
79
+ expect(result.transcripts[0].text).toBe('new');
80
+ expect(new Date(result.transcripts[0].started).getTime()).toBe(T0 + 1_500);
81
+ expect(result.lastUsedTimestamp).toBe(T0 + 2_000);
82
+ });
83
+
84
+ test('drops segments that end before lastUsedTimestamp', ({ expect }) => {
85
+ const segments: WhisperSegment[] = [
86
+ segment({
87
+ text: 'stale',
88
+ start: 0,
89
+ end: 0.5,
90
+ words: [{ word: 'stale', start: 0, end: 0.5 }],
91
+ }),
92
+ segment({
93
+ text: 'fresh',
94
+ start: 1,
95
+ end: 2,
96
+ words: [{ word: 'fresh', start: 1.5, end: 2 }],
97
+ }),
98
+ ];
99
+
100
+ const result = alignWhisperSegments(segments, [chunk(0)], T0 + 1_000);
101
+ expect(result.transcripts).toHaveLength(1);
102
+ expect(result.transcripts[0].text).toBe('fresh');
103
+ });
104
+ });
@@ -7,15 +7,15 @@ import { WaveFile } from 'wavefile';
7
7
  import { DeferredTask, Trigger } from '@dxos/async';
8
8
  import { type Context, LifecycleState, Resource } from '@dxos/context';
9
9
  import { log } from '@dxos/log';
10
- import { type DataType } from '@dxos/schema';
11
10
  import { trace } from '@dxos/tracing';
11
+ import { type ContentBlock } from '@dxos/types';
12
12
 
13
- import { TRANSCRIPTION_URL } from '../types';
14
- import { mergeFloat64Arrays } from '../util';
13
+ import { TRANSCRIPTION_URL } from '#types';
15
14
 
15
+ import { mergeFloat64Arrays } from '../util';
16
16
  import { type AudioChunk, type AudioRecorder } from './audio-recorder';
17
17
 
18
- type WhisperWord = {
18
+ export type WhisperWord = {
19
19
  word: string;
20
20
 
21
21
  /**
@@ -29,7 +29,7 @@ type WhisperWord = {
29
29
  end: number;
30
30
  };
31
31
 
32
- type WhisperSegment = {
32
+ export type WhisperSegment = {
33
33
  text: string;
34
34
 
35
35
  /**
@@ -61,16 +61,32 @@ export type TranscribeConfig = {
61
61
  * This is needed to catch the beginning of the user's speech.
62
62
  */
63
63
  prefixBufferChunksAmount: number;
64
+
65
+ /**
66
+ * Override the transcription endpoint base URL.
67
+ * Defaults to the DXOS calls service.
68
+ */
69
+ endpoint?: string;
64
70
  };
65
71
 
66
- export type TranscriberParams = {
72
+ /**
73
+ * Function that converts a base64-encoded WAV payload into Whisper segments.
74
+ * Allows callers to swap in alternative providers or mock the transport.
75
+ */
76
+ export type TranscribeFn = (audio: string) => Promise<WhisperSegment[]>;
77
+
78
+ export type TranscriberProps = {
67
79
  config: TranscribeConfig;
68
80
  recorder: AudioRecorder;
69
81
  /**
70
82
  * Callback to handle the transcribed segments, after all segment transformers are applied.
71
83
  * @param segments - The transcribed segments.
72
84
  */
73
- onSegments: (segments: DataType.MessageBlock.Transcript[]) => Promise<void>;
85
+ onSegments: (segments: ContentBlock.Transcript[]) => Promise<void>;
86
+ /**
87
+ * Optional override of the transcription transport. When provided, supersedes `config.endpoint`.
88
+ */
89
+ transcribe?: TranscribeFn;
74
90
  };
75
91
 
76
92
  /**
@@ -86,16 +102,18 @@ export class Transcriber extends Resource {
86
102
 
87
103
  private readonly _config: TranscribeConfig;
88
104
  private readonly _recorder: AudioRecorder;
89
- private readonly _onSegments: TranscriberParams['onSegments'];
105
+ private readonly _onSegments: TranscriberProps['onSegments'];
106
+ private readonly _transcribeFn?: TranscribeFn;
90
107
 
91
108
  private _recording = false;
92
109
  private _transcribeTask?: DeferredTask = undefined;
93
110
 
94
- constructor({ config, recorder, onSegments }: TranscriberParams) {
111
+ constructor({ config, recorder, onSegments, transcribe }: TranscriberProps) {
95
112
  super();
96
113
  this._config = config;
97
114
  this._recorder = recorder;
98
115
  this._onSegments = onSegments;
116
+ this._transcribeFn = transcribe;
99
117
  }
100
118
 
101
119
  protected override async _open(ctx: Context): Promise<void> {
@@ -192,67 +210,90 @@ export class Transcriber extends Resource {
192
210
  @trace.span({ showInBrowserTimeline: true })
193
211
  private async _fetchTranscription(audio: string): Promise<WhisperSegment[]> {
194
212
  if (audio.length === 0) {
195
- this._audioChunks = [];
196
213
  throw new Error('No audio to send for transcribing');
197
214
  }
198
215
 
199
- const response = await fetch(`${TRANSCRIPTION_URL}/transcribe`, {
200
- method: 'POST',
201
- body: JSON.stringify({ audio }),
202
- headers: {
203
- 'Content-Type': 'application/json',
204
- },
205
- });
206
-
207
- if (!response.ok) {
208
- this._audioChunks = [];
209
- throw new Error(`Transcription failed: ${response.statusText}`);
216
+ let segments: unknown;
217
+ if (this._transcribeFn) {
218
+ segments = await this._transcribeFn(audio);
219
+ } else {
220
+ const endpoint = this._config.endpoint ?? TRANSCRIPTION_URL;
221
+ const response = await fetch(`${endpoint}/transcribe`, {
222
+ method: 'POST',
223
+ body: JSON.stringify({ audio }),
224
+ headers: {
225
+ 'Content-Type': 'application/json',
226
+ },
227
+ });
228
+
229
+ if (!response.ok) {
230
+ throw new Error(`Transcription failed: ${response.statusText}`);
231
+ }
232
+
233
+ ({ segments } = (await response.json()) as { segments?: unknown });
210
234
  }
211
235
 
212
- const { segments } = (await response.json()) as {
213
- segments: WhisperSegment[];
214
- };
236
+ if (!Array.isArray(segments)) {
237
+ throw new Error('Transcription response payload is invalid');
238
+ }
215
239
 
216
- log.info('transcription response', {
217
- segments: segments.length,
218
- string: segments.map((segments) => segments.text).join(' '),
219
- });
240
+ log.info('transcription response', { segments: segments.length });
220
241
 
221
- return segments;
242
+ return segments as WhisperSegment[];
222
243
  }
223
244
 
224
- private _alignSegments(segments: WhisperSegment[], originalChunks: AudioChunk[]): DataType.MessageBlock.Transcript[] {
225
- // Absolute zero for all relative timestamps in the segments.
226
- const zeroTimestamp = originalChunks.at(0)!.timestamp;
227
-
228
- const filteredSegments = segments
229
- // Use segments that end after the last used timestamp.
230
- // Segments could overlap, so we need to filter words that starts after the last used timestamp.
231
- .filter((segment) => zeroTimestamp + segment.end * 1_000 >= this._lastUsedTimestamp)
232
- // Use words that starts after the last used timestamp.
233
- .map((segment) => {
234
- const words = segment.words.filter((word) => zeroTimestamp + word.start * 1_000 >= this._lastUsedTimestamp);
235
- return {
236
- ...segment,
237
- words,
238
- text: words.map((word) => word.word).join(''),
239
- start: words.at(0)?.start ?? segment.end,
240
- };
241
- })
242
- .filter((segment) => segment.words.length > 0);
243
-
244
- if (filteredSegments.length === 0) {
245
- return [];
245
+ private _alignSegments(segments: WhisperSegment[], originalChunks: AudioChunk[]): ContentBlock.Transcript[] {
246
+ const result = alignWhisperSegments(segments, originalChunks, this._lastUsedTimestamp);
247
+ if (result.lastUsedTimestamp !== undefined) {
248
+ this._lastUsedTimestamp = result.lastUsedTimestamp;
246
249
  }
250
+ return result.transcripts;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Pure helper that filters and absolute-timestamps Whisper segments against a chunk timeline.
256
+ * Exported for testing.
257
+ *
258
+ * @param segments - Raw segments returned by the Whisper service for `originalChunks`.
259
+ * @param originalChunks - Chunks fed into the transcription request (used for the zero timestamp).
260
+ * @param lastUsedTimestamp - Most recent absolute timestamp already emitted; segments whose end
261
+ * falls strictly before this point are deduped out, as are individual words whose start does.
262
+ */
263
+ export const alignWhisperSegments = (
264
+ segments: WhisperSegment[],
265
+ originalChunks: AudioChunk[],
266
+ lastUsedTimestamp: number,
267
+ ): { transcripts: ContentBlock.Transcript[]; lastUsedTimestamp?: number } => {
268
+ if (originalChunks.length === 0) {
269
+ return { transcripts: [] };
270
+ }
247
271
 
248
- // Update last timestamp.
249
- this._lastUsedTimestamp = zeroTimestamp + filteredSegments.at(-1)!.end * 1_000;
272
+ const zeroTimestamp = originalChunks[0].timestamp;
273
+
274
+ const filteredSegments = segments
275
+ .filter((segment) => zeroTimestamp + segment.end * 1_000 >= lastUsedTimestamp)
276
+ .map((segment) => {
277
+ const words = segment.words.filter((word) => zeroTimestamp + word.start * 1_000 >= lastUsedTimestamp);
278
+ return {
279
+ ...segment,
280
+ words,
281
+ text: words.map((word) => word.word).join(''),
282
+ start: words.at(0)?.start ?? segment.end,
283
+ };
284
+ })
285
+ .filter((segment) => segment.words.length > 0);
286
+
287
+ if (filteredSegments.length === 0) {
288
+ return { transcripts: [] };
289
+ }
250
290
 
251
- // Add absolute timestamp to each segment.
252
- return filteredSegments.map((segment) => ({
291
+ return {
292
+ transcripts: filteredSegments.map((segment) => ({
253
293
  _tag: 'transcript',
254
294
  started: new Date(zeroTimestamp + segment.start * 1_000).toISOString(),
255
295
  text: segment.text.trim(),
256
- }));
257
- }
258
- }
296
+ })),
297
+ lastUsedTimestamp: zeroTimestamp + filteredSegments.at(-1)!.end * 1_000,
298
+ };
299
+ };