@dxos/plugin-transcription 0.8.4-main.c85a9c8dae → 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 (303) hide show
  1. package/dist/lib/{browser/TranscriptionContainer-RBFHP2AV.mjs → neutral/TranscriptionContainer-XNGXNGPX.mjs} +10 -16
  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/{browser/blueprint-definition-C6XA2SVZ.mjs → neutral/blueprint-definition-HOXDG5QP.mjs} +4 -7
  8. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs.map +7 -0
  9. package/dist/lib/{browser → neutral}/blueprints/index.mjs +1 -2
  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/{browser/chunk-2B74LZZD.mjs → neutral/chunk-GL4RS533.mjs} +2 -2
  13. package/dist/lib/{browser/chunk-2B74LZZD.mjs.map → neutral/chunk-GL4RS533.mjs.map} +3 -3
  14. package/dist/lib/{browser/chunk-HOOPZ7EM.mjs → neutral/chunk-KAEXRX3Y.mjs} +1 -1
  15. package/dist/lib/{browser/chunk-EV5ZYKET.mjs → neutral/chunk-LS3R7EQI.mjs} +74 -104
  16. package/dist/lib/neutral/chunk-LS3R7EQI.mjs.map +7 -0
  17. package/dist/lib/neutral/chunk-MLRJWAQH.mjs +179 -0
  18. package/dist/lib/neutral/chunk-MLRJWAQH.mjs.map +7 -0
  19. package/dist/lib/neutral/chunk-NNR3EYR2.mjs +124 -0
  20. package/dist/lib/neutral/chunk-NNR3EYR2.mjs.map +7 -0
  21. package/dist/lib/neutral/chunk-NQQK7UZQ.mjs +244 -0
  22. package/dist/lib/neutral/chunk-NQQK7UZQ.mjs.map +7 -0
  23. package/dist/lib/neutral/chunk-WVEZMGBT.mjs +8 -0
  24. package/dist/lib/neutral/chunk-WVEZMGBT.mjs.map +7 -0
  25. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs +30 -0
  26. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
  27. package/dist/lib/neutral/components/index.mjs +207 -0
  28. package/dist/lib/neutral/components/index.mjs.map +7 -0
  29. package/dist/lib/neutral/containers/index.mjs +9 -0
  30. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  31. package/dist/lib/neutral/create-2D6S4IMQ.mjs +21 -0
  32. package/dist/lib/neutral/create-2D6S4IMQ.mjs.map +7 -0
  33. package/dist/lib/neutral/hooks/index.mjs +17 -0
  34. package/dist/lib/neutral/index.mjs +44 -0
  35. package/dist/lib/neutral/meta.json +1 -0
  36. package/dist/lib/neutral/meta.mjs +8 -0
  37. package/dist/lib/neutral/open-2QB6IIET.mjs +27 -0
  38. package/dist/lib/neutral/open-2QB6IIET.mjs.map +7 -0
  39. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
  40. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
  41. package/dist/lib/neutral/operations/index.mjs +8 -0
  42. package/dist/lib/neutral/plugin.mjs +16 -0
  43. package/dist/lib/neutral/plugin.mjs.map +7 -0
  44. package/dist/lib/neutral/react-surface-QF4MUMWG.mjs +26 -0
  45. package/dist/lib/neutral/react-surface-QF4MUMWG.mjs.map +7 -0
  46. package/dist/lib/neutral/summarize-CJPVKWYZ.mjs +78 -0
  47. package/dist/lib/neutral/summarize-CJPVKWYZ.mjs.map +7 -0
  48. package/dist/lib/neutral/text-content-LHSUG2D2.mjs +30 -0
  49. package/dist/lib/neutral/text-content-LHSUG2D2.mjs.map +7 -0
  50. package/dist/lib/{browser/transcriber-WCPHHJYY.mjs → neutral/transcriber-M3R6HPTZ.mjs} +8 -10
  51. package/dist/lib/neutral/transcriber-M3R6HPTZ.mjs.map +7 -0
  52. package/dist/lib/neutral/translations.mjs +22 -0
  53. package/dist/lib/neutral/translations.mjs.map +7 -0
  54. package/dist/lib/neutral/types/index.mjs +12 -0
  55. package/dist/lib/neutral/types/index.mjs.map +7 -0
  56. package/dist/types/src/TranscriptionPlugin.d.ts +1 -0
  57. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  58. package/dist/types/src/TranscriptionPlugin.node.d.ts +4 -0
  59. package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -0
  60. package/dist/types/src/TranscriptionPlugin.test.d.ts +2 -0
  61. package/dist/types/src/TranscriptionPlugin.test.d.ts.map +1 -0
  62. package/dist/types/src/blueprints/transcription-blueprint.d.ts +2 -2
  63. package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -1
  64. package/dist/types/src/capabilities/blueprint-definition.d.ts +6 -0
  65. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  66. package/dist/types/src/capabilities/index.d.ts +10 -4
  67. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  68. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  69. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  71. package/dist/types/src/capabilities/text-content.d.ts +8 -0
  72. package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
  73. package/dist/types/src/capabilities/{transcriber/transcriber.d.ts → transcriber.d.ts} +1 -1
  74. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -0
  75. package/dist/types/src/components/Transcription/Transcription.d.ts +5 -4
  76. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -1
  77. package/dist/types/src/components/Transcription/Transcription.stories.d.ts +10 -10
  78. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -1
  79. package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -1
  80. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +3 -3
  81. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +1 -1
  82. package/dist/types/src/containers/TranscriptionContainer/index.d.ts +1 -2
  83. package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +1 -1
  84. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  85. package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
  86. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
  87. package/dist/types/src/hooks/useTranscriber.d.ts +2 -2
  88. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  89. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  90. package/dist/types/src/index.d.ts +2 -3
  91. package/dist/types/src/index.d.ts.map +1 -1
  92. package/dist/types/src/model/model.d.ts +2 -2
  93. package/dist/types/src/model/model.d.ts.map +1 -1
  94. package/dist/types/src/{segments-normalization → normalization}/index.d.ts +1 -1
  95. package/dist/types/src/normalization/index.d.ts.map +1 -0
  96. package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -0
  97. package/dist/types/src/{segments-normalization → normalization}/normalization.d.ts +330 -321
  98. package/dist/types/src/normalization/normalization.d.ts.map +1 -0
  99. package/dist/types/src/normalization/normalization.test.d.ts.map +1 -0
  100. package/dist/types/src/normalization/utils.d.ts.map +1 -0
  101. package/dist/types/src/operations/create.d.ts +5 -0
  102. package/dist/types/src/operations/create.d.ts.map +1 -0
  103. package/dist/types/src/operations/index.d.ts +3 -0
  104. package/dist/types/src/operations/index.d.ts.map +1 -0
  105. package/dist/types/src/operations/open.d.ts +5 -0
  106. package/dist/types/src/operations/open.d.ts.map +1 -0
  107. package/dist/types/src/operations/summarize.d.ts +8 -0
  108. package/dist/types/src/operations/summarize.d.ts.map +1 -0
  109. package/dist/types/src/plugin.d.ts +4 -0
  110. package/dist/types/src/plugin.d.ts.map +1 -0
  111. package/dist/types/src/{components/stories → stories}/FileTranscription.stories.d.ts +3 -3
  112. package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -0
  113. package/dist/types/src/{components/stories → stories}/KeyWordRecognition.stories.d.ts +2 -2
  114. package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -0
  115. package/dist/types/src/{components/stories → stories}/MicrophoneTranscription.stories.d.ts +3 -3
  116. package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +1 -0
  117. package/dist/types/src/stories/TranscriptionStory.d.ts +23 -0
  118. package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -0
  119. package/dist/types/src/stories/useIsSpeaking.d.ts.map +1 -0
  120. package/dist/types/src/testing/testing.d.ts.map +1 -1
  121. package/dist/types/src/transcriber/index.d.ts +1 -1
  122. package/dist/types/src/transcriber/index.d.ts.map +1 -1
  123. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  124. package/dist/types/src/transcriber/transcriber.d.ts +57 -2
  125. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  126. package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
  127. package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
  128. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  129. package/dist/types/src/translations.d.ts +7 -8
  130. package/dist/types/src/translations.d.ts.map +1 -1
  131. package/dist/types/src/types/TranscriptOperation.d.ts +917 -0
  132. package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
  133. package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
  134. package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
  135. package/dist/types/src/types/{types.d.ts → defs.d.ts} +1 -1
  136. package/dist/types/src/types/defs.d.ts.map +1 -0
  137. package/dist/types/src/types/index.d.ts +3 -4
  138. package/dist/types/src/types/index.d.ts.map +1 -1
  139. package/dist/types/src/util/array.d.ts.map +1 -1
  140. package/dist/types/src/util/get-time-str.d.ts.map +1 -1
  141. package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
  142. package/dist/types/src/util/render-byline.d.ts.map +1 -1
  143. package/dist/types/tsconfig.tsbuildinfo +1 -1
  144. package/package.json +133 -81
  145. package/src/TranscriptionPlugin.node.ts +20 -0
  146. package/src/TranscriptionPlugin.test.ts +31 -0
  147. package/src/TranscriptionPlugin.tsx +8 -29
  148. package/src/blueprints/transcription-blueprint.ts +5 -9
  149. package/src/capabilities/{blueprint-definition/blueprint-definition.ts → blueprint-definition.ts} +3 -1
  150. package/src/capabilities/index.ts +12 -4
  151. package/src/capabilities/operation-handler.ts +16 -0
  152. package/src/capabilities/react-surface.tsx +31 -0
  153. package/src/capabilities/text-content.ts +33 -0
  154. package/src/capabilities/{transcriber/transcriber.ts → transcriber.ts} +5 -2
  155. package/src/components/Transcription/Transcription.stories.tsx +24 -23
  156. package/src/components/Transcription/Transcription.tsx +30 -29
  157. package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +10 -8
  158. package/src/containers/TranscriptionContainer/index.ts +1 -3
  159. package/src/hooks/useTranscriber.ts +5 -2
  160. package/src/hooks/useVoiceInput.ts +0 -1
  161. package/src/index.ts +4 -5
  162. package/src/meta.ts +1 -1
  163. package/src/model/model.test.ts +6 -6
  164. package/src/model/model.ts +3 -3
  165. package/src/{segments-normalization → normalization}/message-normalizer.ts +1 -5
  166. package/src/normalization/normalization.ts +76 -0
  167. package/src/operations/create.ts +24 -0
  168. package/src/operations/index.ts +11 -0
  169. package/src/operations/open.ts +28 -0
  170. package/src/operations/summarize.ts +101 -0
  171. package/src/plugin.ts +11 -0
  172. package/src/{components/stories → stories}/FileTranscription.stories.tsx +33 -16
  173. package/src/{components/stories → stories}/KeyWordRecognition.stories.tsx +32 -30
  174. package/src/{components/stories → stories}/MicrophoneTranscription.stories.tsx +18 -36
  175. package/src/stories/TranscriptionStory.tsx +93 -0
  176. package/src/testing/testing.ts +8 -18
  177. package/src/transcriber/index.ts +1 -1
  178. package/src/transcriber/transcriber.browser.test.ts +0 -1
  179. package/src/transcriber/transcriber.test.ts +104 -0
  180. package/src/transcriber/transcriber.ts +94 -53
  181. package/src/translations.ts +7 -7
  182. package/src/types/TranscriptOperation.ts +103 -0
  183. package/src/types/TranscriptionCapabilities.ts +39 -0
  184. package/src/types/index.ts +4 -4
  185. package/dist/lib/browser/TranscriptionContainer-RBFHP2AV.mjs.map +0 -7
  186. package/dist/lib/browser/blueprint-definition-C6XA2SVZ.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-EV5ZYKET.mjs.map +0 -7
  188. package/dist/lib/browser/chunk-GH4IIQ6C.mjs +0 -54
  189. package/dist/lib/browser/chunk-GH4IIQ6C.mjs.map +0 -7
  190. package/dist/lib/browser/chunk-O7ILGNBZ.mjs +0 -148
  191. package/dist/lib/browser/chunk-O7ILGNBZ.mjs.map +0 -7
  192. package/dist/lib/browser/chunk-XLMRCOQN.mjs +0 -668
  193. package/dist/lib/browser/chunk-XLMRCOQN.mjs.map +0 -7
  194. package/dist/lib/browser/index.mjs +0 -114
  195. package/dist/lib/browser/index.mjs.map +0 -7
  196. package/dist/lib/browser/meta.json +0 -1
  197. package/dist/lib/browser/operation-resolver-5BLRH4XE.mjs +0 -23
  198. package/dist/lib/browser/operation-resolver-5BLRH4XE.mjs.map +0 -7
  199. package/dist/lib/browser/react-surface-U2SI74JK.mjs +0 -36
  200. package/dist/lib/browser/react-surface-U2SI74JK.mjs.map +0 -7
  201. package/dist/lib/browser/transcriber-WCPHHJYY.mjs.map +0 -7
  202. package/dist/lib/browser/types/index.mjs +0 -15
  203. package/dist/lib/node-esm/TranscriptionContainer-3OOID5KH.mjs +0 -42
  204. package/dist/lib/node-esm/TranscriptionContainer-3OOID5KH.mjs.map +0 -7
  205. package/dist/lib/node-esm/blueprint-definition-TLXDUXIQ.mjs +0 -19
  206. package/dist/lib/node-esm/blueprint-definition-TLXDUXIQ.mjs.map +0 -7
  207. package/dist/lib/node-esm/blueprints/index.mjs +0 -10
  208. package/dist/lib/node-esm/chunk-6GW5NZYG.mjs +0 -55
  209. package/dist/lib/node-esm/chunk-6GW5NZYG.mjs.map +0 -7
  210. package/dist/lib/node-esm/chunk-7UE7G6DE.mjs +0 -461
  211. package/dist/lib/node-esm/chunk-7UE7G6DE.mjs.map +0 -7
  212. package/dist/lib/node-esm/chunk-DYUGYQCB.mjs +0 -669
  213. package/dist/lib/node-esm/chunk-DYUGYQCB.mjs.map +0 -7
  214. package/dist/lib/node-esm/chunk-F47HK7GT.mjs +0 -24
  215. package/dist/lib/node-esm/chunk-F47HK7GT.mjs.map +0 -7
  216. package/dist/lib/node-esm/chunk-H3MXHMQL.mjs +0 -149
  217. package/dist/lib/node-esm/chunk-H3MXHMQL.mjs.map +0 -7
  218. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  219. package/dist/lib/node-esm/chunk-NVM2QUMX.mjs +0 -37
  220. package/dist/lib/node-esm/chunk-NVM2QUMX.mjs.map +0 -7
  221. package/dist/lib/node-esm/index.mjs +0 -115
  222. package/dist/lib/node-esm/index.mjs.map +0 -7
  223. package/dist/lib/node-esm/meta.json +0 -1
  224. package/dist/lib/node-esm/operation-resolver-CVZB7BF4.mjs +0 -24
  225. package/dist/lib/node-esm/operation-resolver-CVZB7BF4.mjs.map +0 -7
  226. package/dist/lib/node-esm/react-surface-2ICJPBPX.mjs +0 -37
  227. package/dist/lib/node-esm/react-surface-2ICJPBPX.mjs.map +0 -7
  228. package/dist/lib/node-esm/transcriber-2RIHUEX4.mjs +0 -62
  229. package/dist/lib/node-esm/transcriber-2RIHUEX4.mjs.map +0 -7
  230. package/dist/lib/node-esm/types/index.mjs +0 -16
  231. package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts +0 -6
  232. package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts.map +0 -1
  233. package/dist/types/src/capabilities/blueprint-definition/index.d.ts +0 -3
  234. package/dist/types/src/capabilities/blueprint-definition/index.d.ts.map +0 -1
  235. package/dist/types/src/capabilities/operation-handler/index.d.ts +0 -3
  236. package/dist/types/src/capabilities/operation-handler/index.d.ts.map +0 -1
  237. package/dist/types/src/capabilities/operation-handler/operation-handler.d.ts +0 -5
  238. package/dist/types/src/capabilities/operation-handler/operation-handler.d.ts.map +0 -1
  239. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  240. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  241. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  242. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  243. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  244. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  245. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  246. package/dist/types/src/capabilities/transcriber/index.d.ts +0 -3
  247. package/dist/types/src/capabilities/transcriber/index.d.ts.map +0 -1
  248. package/dist/types/src/capabilities/transcriber/transcriber.d.ts.map +0 -1
  249. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +0 -1
  250. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +0 -1
  251. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
  252. package/dist/types/src/components/stories/TranscriptionStory.d.ts +0 -12
  253. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +0 -1
  254. package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +0 -1
  255. package/dist/types/src/functions/index.d.ts +0 -18
  256. package/dist/types/src/functions/index.d.ts.map +0 -1
  257. package/dist/types/src/functions/open.d.ts +0 -11
  258. package/dist/types/src/functions/open.d.ts.map +0 -1
  259. package/dist/types/src/functions/summarize.d.ts +0 -11
  260. package/dist/types/src/functions/summarize.d.ts.map +0 -1
  261. package/dist/types/src/segments-normalization/index.d.ts.map +0 -1
  262. package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +0 -1
  263. package/dist/types/src/segments-normalization/normalization.d.ts.map +0 -1
  264. package/dist/types/src/segments-normalization/normalization.test.d.ts.map +0 -1
  265. package/dist/types/src/segments-normalization/utils.d.ts.map +0 -1
  266. package/dist/types/src/types/TranscriptAction.d.ts +0 -25
  267. package/dist/types/src/types/TranscriptAction.d.ts.map +0 -1
  268. package/dist/types/src/types/capabilities.d.ts +0 -18
  269. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  270. package/dist/types/src/types/types.d.ts.map +0 -1
  271. package/src/capabilities/blueprint-definition/index.ts +0 -7
  272. package/src/capabilities/operation-handler/index.ts +0 -7
  273. package/src/capabilities/operation-handler/operation-handler.ts +0 -25
  274. package/src/capabilities/operation-resolver/index.ts +0 -7
  275. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -25
  276. package/src/capabilities/react-surface/index.ts +0 -7
  277. package/src/capabilities/react-surface/react-surface.tsx +0 -28
  278. package/src/capabilities/transcriber/index.ts +0 -7
  279. package/src/components/stories/TranscriptionStory.tsx +0 -41
  280. package/src/functions/index.ts +0 -11
  281. package/src/functions/open.ts +0 -37
  282. package/src/functions/summarize.ts +0 -103
  283. package/src/segments-normalization/normalization.ts +0 -115
  284. package/src/types/TranscriptAction.ts +0 -32
  285. package/src/types/capabilities.ts +0 -33
  286. package/dist/lib/{browser → neutral}/blueprints/index.mjs.map +0 -0
  287. package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  288. package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
  289. package/dist/lib/{browser/chunk-HOOPZ7EM.mjs.map → neutral/chunk-KAEXRX3Y.mjs.map} +0 -0
  290. package/dist/lib/{browser/types → neutral/hooks}/index.mjs.map +0 -0
  291. package/dist/lib/{node-esm/blueprints → neutral}/index.mjs.map +0 -0
  292. package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
  293. package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
  294. package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
  295. package/dist/types/src/{segments-normalization → normalization}/message-normalizer.d.ts +0 -0
  296. package/dist/types/src/{segments-normalization → normalization}/normalization.test.d.ts +0 -0
  297. package/dist/types/src/{segments-normalization → normalization}/utils.d.ts +0 -0
  298. package/dist/types/src/{components/stories → stories}/useIsSpeaking.d.ts +0 -0
  299. package/src/{segments-normalization → normalization}/index.ts +1 -1
  300. package/src/{segments-normalization → normalization}/normalization.test.ts +1 -1
  301. /package/src/{segments-normalization → normalization}/utils.ts +0 -0
  302. /package/src/{components/stories → stories}/useIsSpeaking.tsx +0 -0
  303. /package/src/types/{types.ts → defs.ts} +0 -0
@@ -11,25 +11,26 @@ import { AppActivationEvents } from '@dxos/app-toolkit';
11
11
  import { scheduleTask } from '@dxos/async';
12
12
  import { Context } from '@dxos/context';
13
13
  import { Obj } from '@dxos/echo';
14
- import { createQueueDXN } from '@dxos/echo/internal';
15
14
  import { MemoryQueue } from '@dxos/echo-db';
15
+ import { createQueueDXN } from '@dxos/echo/internal';
16
16
  import { FunctionExecutor, ServiceContainer } from '@dxos/functions-runtime';
17
17
  import { log } from '@dxos/log';
18
- import { ClientPlugin } from '@dxos/plugin-client';
19
- import { PreviewPlugin } from '@dxos/plugin-preview';
18
+ import { ClientPlugin } from '@dxos/plugin-client/plugin';
19
+ import { initializeIdentity } from '@dxos/plugin-client/testing';
20
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
20
21
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
21
- import { withLayout, withTheme } from '@dxos/react-ui/testing';
22
+ import { withLayout } from '@dxos/react-ui/testing';
22
23
  import { TestSchema } from '@dxos/schema/testing';
23
24
  import { type Actor, Message, Organization, Person } from '@dxos/types';
24
25
  import { seedTestData } from '@dxos/types/testing';
25
26
 
26
- import { useAudioFile, useQueueModelAdapter, useTranscriber } from '../../hooks';
27
- import { MessageNormalizer, getActorId } from '../../segments-normalization';
28
- import { TestItem } from '../../testing';
29
- import { type MediaStreamRecorderProps, type TranscriberProps } from '../../transcriber';
30
- import { TranscriptionPlugin } from '../../TranscriptionPlugin';
31
- import { renderByline } from '../../util';
27
+ import { useAudioFile, useQueueModelAdapter, useTranscriber } from '#hooks';
28
+ import { TestItem } from '#testing';
32
29
 
30
+ import { MessageNormalizer, getActorId } from '../normalization';
31
+ import { type MediaStreamRecorderProps, type TranscriberProps } from '../transcriber';
32
+ import { TranscriptionPlugin } from '../TranscriptionPlugin';
33
+ import { renderByline } from '../util';
33
34
  import { TranscriptionStory } from './TranscriptionStory';
34
35
  import { useIsSpeaking } from './useIsSpeaking';
35
36
 
@@ -51,8 +52,26 @@ const AudioFile = ({
51
52
  const [running, setRunning] = useState(false);
52
53
  const actor: Actor.Actor = useMemo(() => ({ name: 'You' }), []);
53
54
 
55
+ // Optional uploaded file overrides the default URL.
56
+ // The useEffect below revokes the previous URL whenever it changes (and on unmount).
57
+ const [uploadedUrl, setUploadedUrl] = useState<string>();
58
+ useEffect(() => {
59
+ return () => {
60
+ if (uploadedUrl) {
61
+ URL.revokeObjectURL(uploadedUrl);
62
+ }
63
+ };
64
+ }, [uploadedUrl]);
65
+
66
+ const handleUpload = useCallback((file: File) => {
67
+ setRunning(false);
68
+ setUploadedUrl(URL.createObjectURL(file));
69
+ }, []);
70
+
71
+ const sourceUrl = uploadedUrl ?? audioUrl;
72
+
54
73
  // Audio.
55
- const { audio, track, stream } = useAudioFile(audioUrl, audioConstraints);
74
+ const { audio, track, stream } = useAudioFile(sourceUrl, audioConstraints);
56
75
 
57
76
  // Speaking.
58
77
  const isSpeaking = detectSpeaking ? useIsSpeaking(track) : true;
@@ -179,6 +198,7 @@ const AudioFile = ({
179
198
  running={running}
180
199
  onRunningChange={setRunning}
181
200
  audioRef={ref}
201
+ onUpload={handleUpload}
182
202
  />
183
203
  );
184
204
  };
@@ -186,7 +206,6 @@ const AudioFile = ({
186
206
  const meta = {
187
207
  title: 'plugins/plugin-transcription/components/FileTranscription',
188
208
  decorators: [
189
- withTheme(),
190
209
  withLayout({ layout: 'column' }),
191
210
  withPluginManager({
192
211
  plugins: [
@@ -196,10 +215,8 @@ const meta = {
196
215
  types: [TestItem, Person.Person, Organization.Organization, TestSchema.DocumentType],
197
216
  onClientInitialized: ({ client }) =>
198
217
  Effect.gen(function* () {
199
- yield* Effect.promise(() => client.halo.createIdentity());
200
- yield* Effect.promise(() => client.spaces.waitUntilReady());
201
- yield* Effect.promise(() => client.spaces.default.waitUntilReady());
202
- yield* Effect.promise(() => seedTestData(client.spaces.default));
218
+ const { personalSpace } = yield* initializeIdentity(client);
219
+ yield* Effect.promise(() => seedTestData(personalSpace));
203
220
  }),
204
221
  }),
205
222
 
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useEffect, useMemo, useState } from 'react';
7
7
 
8
8
  import { log } from '@dxos/log';
9
- import { Toolbar } from '@dxos/react-ui';
9
+ import { Panel, Toolbar } from '@dxos/react-ui';
10
10
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
11
  import { mx } from '@dxos/ui-theme';
12
12
 
@@ -15,11 +15,11 @@ type Word = {
15
15
  matched: boolean;
16
16
  };
17
17
 
18
- type StoryProps = {
18
+ type DefaultStoryProps = {
19
19
  keywords: string[];
20
20
  };
21
21
 
22
- const DefaultStory = ({ keywords }: StoryProps) => {
22
+ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
23
23
  const [running, setRunning] = useState(false);
24
24
  const [matchingWords, setMatchingWords] = useState<Word[]>(keywords.map((word) => ({ text: word, matched: false })));
25
25
 
@@ -86,32 +86,34 @@ const DefaultStory = ({ keywords }: StoryProps) => {
86
86
  }, [running, recognition]);
87
87
 
88
88
  return (
89
- <div className={mx('flex flex-col gap-2 w-[30rem]')}>
90
- <Toolbar.Root>
91
- <Toolbar.IconButton
92
- iconOnly
93
- icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
94
- label={running ? 'Pause' : 'Play'}
95
- onClick={() => setRunning((running) => !running)}
96
- />
97
- </Toolbar.Root>
98
-
99
- <div className='flex flex-wrap'>
100
- {matchingWords.map((word, index) => (
101
- <span
102
- key={index}
103
- className={mx(
104
- 'px-3 py-1 m-2 rounded-sm border',
105
- word.matched ? 'bg-red-200 text-red-800 border-red-300' : 'bg-gray-200 text-gray-600 border-gray-300',
106
- )}
107
- >
108
- {word.text}
109
- </span>
110
- ))}
111
- </div>
112
-
113
- <span>{transcript || (running ? 'Listening...' : 'Click play to start speech recognition')}</span>
114
- </div>
89
+ <Panel.Root>
90
+ <Panel.Toolbar asChild>
91
+ <Toolbar.Root>
92
+ <Toolbar.IconButton
93
+ iconOnly
94
+ icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
95
+ label={running ? 'Pause' : 'Play'}
96
+ onClick={() => setRunning((running) => !running)}
97
+ />
98
+ </Toolbar.Root>
99
+ </Panel.Toolbar>
100
+
101
+ <Panel.Content>
102
+ <div className='p-4 grid grid-cols-[repeat(auto-fill,minmax(6rem,1fr))] gap-2'>
103
+ {matchingWords.map((word, index) => (
104
+ <span
105
+ key={index}
106
+ className={mx(
107
+ 'grid place-items-center p-2 aspect-square rounded-sm border border-separator',
108
+ word.matched && 'border-red-300',
109
+ )}
110
+ >
111
+ {word.text}
112
+ </span>
113
+ ))}
114
+ </div>
115
+ </Panel.Content>
116
+ </Panel.Root>
115
117
  );
116
118
  };
117
119
 
@@ -127,6 +129,6 @@ type Story = StoryObj<typeof meta>;
127
129
 
128
130
  export const SpeechRecognitionAPI: Story = {
129
131
  args: {
130
- keywords: ['kai', 'computer', 'hello'],
132
+ keywords: ['kai', 'computer', 'hello', 'dxos'],
131
133
  },
132
134
  };
@@ -16,26 +16,26 @@ import {
16
16
  processTranscriptMessage,
17
17
  } from '@dxos/assistant/extraction';
18
18
  import { Filter, type Obj } from '@dxos/echo';
19
- import { createQueueDXN } from '@dxos/echo/internal';
20
19
  import { MemoryQueue } from '@dxos/echo-db';
21
- import { FunctionExecutor, ServiceContainer } from '@dxos/functions-runtime';
20
+ import { createQueueDXN } from '@dxos/echo/internal';
22
21
  import { invariant } from '@dxos/invariant';
23
22
  import { log } from '@dxos/log';
24
- import { ClientPlugin } from '@dxos/plugin-client';
25
- import { PreviewPlugin } from '@dxos/plugin-preview';
23
+ import { ClientPlugin } from '@dxos/plugin-client/plugin';
24
+ import { initializeIdentity } from '@dxos/plugin-client/testing';
25
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
26
26
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
27
- import { IndexKind, useSpace } from '@dxos/react-client/echo';
28
- import { withLayout, withTheme } from '@dxos/react-ui/testing';
27
+ import { IndexKind, useSpaces } from '@dxos/react-client/echo';
28
+ import { withLayout } from '@dxos/react-ui/testing';
29
29
  import { TestSchema } from '@dxos/schema/testing';
30
30
  import { Message, Organization, Person } from '@dxos/types';
31
31
  import { seedTestData } from '@dxos/types/testing';
32
32
 
33
- import { useAudioTrack, useQueueModelAdapter, useTranscriber } from '../../hooks';
34
- import { TestItem } from '../../testing';
35
- import { type MediaStreamRecorderProps, type TranscriberProps } from '../../transcriber';
36
- import { TranscriptionPlugin } from '../../TranscriptionPlugin';
37
- import { renderByline } from '../../util';
33
+ import { useAudioTrack, useQueueModelAdapter, useTranscriber } from '#hooks';
34
+ import { TestItem } from '#testing';
38
35
 
36
+ import { type MediaStreamRecorderProps, type TranscriberProps } from '../transcriber';
37
+ import { TranscriptionPlugin } from '../TranscriptionPlugin';
38
+ import { renderByline } from '../util';
39
39
  import { TranscriptionStory } from './TranscriptionStory';
40
40
  import { useIsSpeaking } from './useIsSpeaking';
41
41
 
@@ -48,7 +48,7 @@ const RECORDER_CONFIG = {
48
48
  interval: 200,
49
49
  };
50
50
 
51
- type StoryProps = {
51
+ type DefaultStoryProps = {
52
52
  detectSpeaking?: boolean;
53
53
  transcriberConfig: TranscriberProps['config'];
54
54
  recorderConfig: MediaStreamRecorderProps['config'];
@@ -62,7 +62,7 @@ const DefaultStory = ({
62
62
  transcriberConfig,
63
63
  recorderConfig,
64
64
  audioConstraints,
65
- }: StoryProps) => {
65
+ }: DefaultStoryProps) => {
66
66
  const [running, setRunning] = useState(false);
67
67
 
68
68
  // Audio.
@@ -76,7 +76,7 @@ const DefaultStory = ({
76
76
  const queueDxn = useMemo(() => createQueueDXN(), []);
77
77
  const queue = useMemo(() => new MemoryQueue<Message.Message>(queueDxn), [queueDxn]);
78
78
  const model = useQueueModelAdapter(renderByline([]), queue);
79
- const space = useSpace();
79
+ const [space] = useSpaces();
80
80
 
81
81
  useEffect(() => {
82
82
  if (!space) {
@@ -85,13 +85,12 @@ const DefaultStory = ({
85
85
  }, [space]);
86
86
 
87
87
  // Entity extraction.
88
- const { extractionFunction, executor, objects } = useMemo(() => {
88
+ const { extractionFunction, objects } = useMemo(() => {
89
89
  if (!space) {
90
90
  log.warn('no space');
91
91
  return {};
92
92
  }
93
93
 
94
- let executor: FunctionExecutor | undefined;
95
94
  let extractionFunction: ExtractionFunction | undefined;
96
95
  let objects: Promise<Obj.Unknown[]> | undefined;
97
96
 
@@ -111,20 +110,8 @@ const DefaultStory = ({
111
110
  )
112
111
  .run();
113
112
  }
114
- if (entityExtraction !== 'none') {
115
- executor = new FunctionExecutor(
116
- new ServiceContainer().setServices({
117
- // ai: {
118
- // client: new Edge AiServiceClient({
119
- // endpoint: AI_SERVICE_ENDPOINT.REMOTE,
120
- // }),
121
- // },
122
- // database: { db: space!.db },
123
- }),
124
- );
125
- }
126
113
 
127
- return { extractionFunction, executor, objects };
114
+ return { extractionFunction, objects };
128
115
  }, [entityExtraction, space]);
129
116
 
130
117
  // Transcriber.
@@ -142,9 +129,7 @@ const DefaultStory = ({
142
129
 
143
130
  if (entityExtraction !== 'none') {
144
131
  invariant(extractionFunction, 'extractionFunction is required');
145
- invariant(executor, 'executor is required');
146
132
  const result = await processTranscriptMessage({
147
- executor,
148
133
  function: extractionFunction,
149
134
  input: {
150
135
  message,
@@ -193,7 +178,6 @@ const meta = {
193
178
  title: 'plugins/plugin-transcription/components/MicrophoneTranscription',
194
179
  render: DefaultStory,
195
180
  decorators: [
196
- withTheme(),
197
181
  withLayout({ layout: 'column' }),
198
182
  withPluginManager({
199
183
  plugins: [
@@ -203,9 +187,7 @@ const meta = {
203
187
  types: [TestItem, Person.Person, Organization.Organization, TestSchema.DocumentType],
204
188
  onClientInitialized: ({ client }) =>
205
189
  Effect.gen(function* () {
206
- yield* Effect.promise(() => client.halo.createIdentity());
207
- yield* Effect.promise(() => client.spaces.waitUntilReady());
208
- yield* Effect.promise(() => client.spaces.default.waitUntilReady());
190
+ const { personalSpace } = yield* initializeIdentity(client);
209
191
  // TODO(mykola): Make API easier to use.
210
192
  // TODO(mykola): Delete after enabling vector indexing by default.
211
193
  // Enable vector indexing.
@@ -221,7 +203,7 @@ const meta = {
221
203
  }),
222
204
  );
223
205
  yield* Effect.promise(() => client.services.services.QueryService!.reindex());
224
- yield* Effect.promise(() => seedTestData(client.spaces.default));
206
+ yield* Effect.promise(() => seedTestData(personalSpace));
225
207
  }),
226
208
  }),
227
209
 
@@ -0,0 +1,93 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import React, {
8
+ type ChangeEvent,
9
+ type Dispatch,
10
+ type FC,
11
+ type ReactNode,
12
+ type RefObject,
13
+ type SetStateAction,
14
+ useRef,
15
+ } from 'react';
16
+
17
+ import { IconButton, ScrollContainer, Toolbar } from '@dxos/react-ui';
18
+ import { type Message } from '@dxos/types';
19
+
20
+ import { Transcription } from '../components';
21
+ import { type SerializationModel } from '../model';
22
+
23
+ /**
24
+ * Story wrapper that renders a transcript with playback controls, an optional audio file
25
+ * upload affordance, and a toolbar slot for additional controls.
26
+ *
27
+ * @param onUpload - Callback fired when the user picks a local audio file via the upload button.
28
+ * @param uploadAccept - HTML `accept` filter for the hidden file input. Defaults to `audio/*`.
29
+ * @param toolbarSlot - Extra content rendered after the upload button inside the toolbar.
30
+ */
31
+ export const TranscriptionStory: FC<{
32
+ model: SerializationModel<Message.Message>;
33
+ disabled?: boolean;
34
+ running: boolean;
35
+ onRunningChange: Dispatch<SetStateAction<boolean>>;
36
+ audioRef?: RefObject<HTMLAudioElement | null>;
37
+ onUpload?: (file: File) => void;
38
+ uploadAccept?: string;
39
+ toolbarSlot?: ReactNode;
40
+ }> = ({ model, running, onRunningChange, audioRef, disabled, onUpload, uploadAccept = 'audio/*', toolbarSlot }) => {
41
+ const fileInputRef = useRef<HTMLInputElement>(null);
42
+
43
+ const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
44
+ const file = event.target.files?.[0];
45
+ if (file && onUpload) {
46
+ onUpload(file);
47
+ }
48
+ // Reset so the same file can be re-selected.
49
+ event.target.value = '';
50
+ };
51
+
52
+ return (
53
+ <>
54
+ {audioRef && <audio ref={audioRef} autoPlay />}
55
+ <Toolbar.Root>
56
+ <IconButton
57
+ iconOnly
58
+ disabled={disabled}
59
+ icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
60
+ label={running ? 'Pause' : 'Play'}
61
+ onClick={() => onRunningChange((running) => !running)}
62
+ />
63
+ {onUpload && (
64
+ <>
65
+ <input
66
+ ref={fileInputRef}
67
+ type='file'
68
+ accept={uploadAccept}
69
+ disabled={disabled}
70
+ className='hidden'
71
+ onChange={handleFileChange}
72
+ />
73
+ <IconButton
74
+ iconOnly
75
+ disabled={disabled}
76
+ icon='ph--upload--regular'
77
+ label='Upload audio'
78
+ onClick={() => fileInputRef.current?.click()}
79
+ />
80
+ </>
81
+ )}
82
+ {toolbarSlot}
83
+ </Toolbar.Root>
84
+ <ScrollContainer.Root pin>
85
+ <ScrollContainer.Content>
86
+ <ScrollContainer.Viewport>
87
+ <Transcription model={model} />
88
+ </ScrollContainer.Viewport>
89
+ </ScrollContainer.Content>
90
+ </ScrollContainer.Root>
91
+ </>
92
+ );
93
+ };
@@ -11,10 +11,9 @@ 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
13
  import { createQueueDXN } from '@dxos/echo/internal';
14
- import { FunctionExecutor, ServiceContainer } from '@dxos/functions-runtime';
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
18
  import { TestSchema } from '@dxos/schema/testing';
20
19
  import { type ContentBlock, Message, Organization, Person } from '@dxos/types';
@@ -32,7 +31,7 @@ export const TestItem = Schema.Struct({
32
31
  }),
33
32
  }).pipe(
34
33
  Type.object({
35
- typename: 'dxos.org/type/Test',
34
+ typename: 'org.dxos.type.test',
36
35
  version: '0.1.0',
37
36
  }),
38
37
  );
@@ -50,8 +49,8 @@ export class MessageBuilder extends AbstractMessageBuilder {
50
49
 
51
50
  users = Array.from({ length: 5 }, () => ({
52
51
  identityDid: IdentityDid.random().toString(),
53
- name: faker.person.fullName(),
54
- email: faker.internet.email(),
52
+ name: random.person.fullName(),
53
+ email: random.internet.email(),
55
54
  }));
56
55
 
57
56
  start = new Date(Date.now() - 24 * 60 * 60 * 10_000);
@@ -63,19 +62,19 @@ export class MessageBuilder extends AbstractMessageBuilder {
63
62
  override async createMessage(numSegments = 1): Promise<Message.Message> {
64
63
  return Obj.make(Message.Message, {
65
64
  created: this.next().toISOString(),
66
- sender: faker.helpers.arrayElement(this.users),
65
+ sender: random.helpers.arrayElement(this.users),
67
66
  blocks: Array.from({ length: numSegments }).map(() => this.createBlock()),
68
67
  });
69
68
  }
70
69
 
71
70
  createBlock(): ContentBlock.Transcript {
72
- let text = faker.lorem.paragraph();
71
+ let text = random.lorem.paragraph();
73
72
  if (this._space) {
74
- const label = faker.commerce.productName();
73
+ const label = random.commerce.productName();
75
74
  const obj = this._space.db.add(
76
75
  Obj.make(TestItem, {
77
76
  title: label,
78
- description: faker.lorem.paragraph(),
77
+ description: random.lorem.paragraph(),
79
78
  }),
80
79
  );
81
80
  const dxn = Ref.make(obj).dxn.toString();
@@ -99,14 +98,6 @@ export class MessageBuilder extends AbstractMessageBuilder {
99
98
 
100
99
  // TODO(burdon): Reconcile with BlockBuilder.
101
100
  class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
102
- executor = new FunctionExecutor(
103
- new ServiceContainer().setServices({
104
- // ai: {
105
- // client: this.AiService,
106
- // },
107
- }),
108
- );
109
-
110
101
  space: Space | undefined;
111
102
  currentMessage: number = 0;
112
103
  transcriptMessages: Message.Message[] = [];
@@ -139,7 +130,6 @@ class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
139
130
 
140
131
  const { message: enhancedMessage } = await processTranscriptMessage({
141
132
  input: { message },
142
- executor: this.executor,
143
133
  function: extractionAnthropicFunction,
144
134
  });
145
135
 
@@ -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';
@@ -4,7 +4,6 @@
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
 
@@ -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
+ });