@dxos/plugin-transcription 0.8.4-main.fd6878d → 0.9.0

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 (367) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +401 -0
  3. package/README.md +1 -1
  4. package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs +54 -0
  5. package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs.map +7 -0
  6. package/dist/lib/neutral/TranscriptionPlugin.mjs +46 -0
  7. package/dist/lib/neutral/TranscriptionPlugin.mjs.map +7 -0
  8. package/dist/lib/neutral/TranscriptionPlugin.node.mjs +25 -0
  9. package/dist/lib/neutral/TranscriptionPlugin.node.mjs.map +7 -0
  10. package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs +21 -0
  11. package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs.map +7 -0
  12. package/dist/lib/neutral/blueprint-definition-DA4QFRYJ.mjs +15 -0
  13. package/dist/lib/neutral/blueprint-definition-DA4QFRYJ.mjs.map +7 -0
  14. package/dist/lib/neutral/blueprints/index.mjs +8 -0
  15. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  16. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  17. package/dist/lib/neutral/chunk-FHNI26QW.mjs +45 -0
  18. package/dist/lib/neutral/chunk-FHNI26QW.mjs.map +7 -0
  19. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  20. package/dist/lib/neutral/chunk-KAEXRX3Y.mjs +35 -0
  21. package/dist/lib/neutral/chunk-KAEXRX3Y.mjs.map +7 -0
  22. package/dist/lib/neutral/chunk-KMT3BMMQ.mjs +124 -0
  23. package/dist/lib/neutral/chunk-KMT3BMMQ.mjs.map +7 -0
  24. package/dist/lib/neutral/chunk-KXJZEYS7.mjs +8 -0
  25. package/dist/lib/neutral/chunk-KXJZEYS7.mjs.map +7 -0
  26. package/dist/lib/neutral/chunk-MLRJWAQH.mjs +179 -0
  27. package/dist/lib/neutral/chunk-MLRJWAQH.mjs.map +7 -0
  28. package/dist/lib/neutral/chunk-TO77KAQK.mjs +298 -0
  29. package/dist/lib/neutral/chunk-TO77KAQK.mjs.map +7 -0
  30. package/dist/lib/{browser/chunk-KJIQQHJB.mjs → neutral/chunk-TX3NQISZ.mjs} +115 -186
  31. package/dist/lib/neutral/chunk-TX3NQISZ.mjs.map +7 -0
  32. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs +30 -0
  33. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
  34. package/dist/lib/neutral/components/index.mjs +269 -0
  35. package/dist/lib/neutral/components/index.mjs.map +7 -0
  36. package/dist/lib/neutral/containers/index.mjs +9 -0
  37. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  38. package/dist/lib/neutral/create-RVR7VC33.mjs +21 -0
  39. package/dist/lib/neutral/create-RVR7VC33.mjs.map +7 -0
  40. package/dist/lib/neutral/hooks/index.mjs +19 -0
  41. package/dist/lib/neutral/index.mjs +44 -0
  42. package/dist/lib/neutral/meta.json +1 -0
  43. package/dist/lib/neutral/meta.mjs +8 -0
  44. package/dist/lib/neutral/meta.mjs.map +7 -0
  45. package/dist/lib/neutral/open-A5KXKRH4.mjs +27 -0
  46. package/dist/lib/neutral/open-A5KXKRH4.mjs.map +7 -0
  47. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
  48. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
  49. package/dist/lib/neutral/operations/index.mjs +8 -0
  50. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  51. package/dist/lib/neutral/plugin.mjs +16 -0
  52. package/dist/lib/neutral/plugin.mjs.map +7 -0
  53. package/dist/lib/neutral/react-surface-YMZRO242.mjs +26 -0
  54. package/dist/lib/neutral/react-surface-YMZRO242.mjs.map +7 -0
  55. package/dist/lib/neutral/summarize-5XPPMVTE.mjs +77 -0
  56. package/dist/lib/neutral/summarize-5XPPMVTE.mjs.map +7 -0
  57. package/dist/lib/neutral/text-content-KBB37HSM.mjs +30 -0
  58. package/dist/lib/neutral/text-content-KBB37HSM.mjs.map +7 -0
  59. package/dist/lib/neutral/transcriber-OGSF3XO5.mjs +59 -0
  60. package/dist/lib/neutral/transcriber-OGSF3XO5.mjs.map +7 -0
  61. package/dist/lib/neutral/translations.mjs +24 -0
  62. package/dist/lib/neutral/translations.mjs.map +7 -0
  63. package/dist/lib/neutral/types/index.mjs +10 -0
  64. package/dist/lib/neutral/types/index.mjs.map +7 -0
  65. package/dist/types/src/TranscriptionPlugin.d.ts +3 -1
  66. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  67. package/dist/types/src/TranscriptionPlugin.node.d.ts +4 -0
  68. package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -0
  69. package/dist/types/src/TranscriptionPlugin.test.d.ts +2 -0
  70. package/dist/types/src/TranscriptionPlugin.test.d.ts.map +1 -0
  71. package/dist/types/src/TranscriptionPlugin.workerd.d.ts +4 -0
  72. package/dist/types/src/TranscriptionPlugin.workerd.d.ts.map +1 -0
  73. package/dist/types/src/blueprints/index.d.ts +2 -0
  74. package/dist/types/src/blueprints/index.d.ts.map +1 -0
  75. package/dist/types/src/blueprints/transcription-blueprint.d.ts +4 -0
  76. package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -0
  77. package/dist/types/src/capabilities/blueprint-definition.d.ts +6 -0
  78. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  79. package/dist/types/src/capabilities/index.d.ts +11 -4
  80. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  81. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  82. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  83. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  84. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  85. package/dist/types/src/capabilities/text-content.d.ts +8 -0
  86. package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
  87. package/dist/types/src/capabilities/transcriber.d.ts +4 -3
  88. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  89. package/dist/types/src/components/Oracle/Oracle.d.ts +24 -0
  90. package/dist/types/src/components/Oracle/Oracle.d.ts.map +1 -0
  91. package/dist/types/src/components/Oracle/Oracle.stories.d.ts +32 -0
  92. package/dist/types/src/components/Oracle/Oracle.stories.d.ts.map +1 -0
  93. package/dist/types/src/components/Oracle/index.d.ts +2 -0
  94. package/dist/types/src/components/Oracle/index.d.ts.map +1 -0
  95. package/dist/types/src/components/Transcription/Transcription.d.ts +11 -0
  96. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -0
  97. package/dist/types/src/components/Transcription/Transcription.stories.d.ts +37 -0
  98. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -0
  99. package/dist/types/src/components/Transcription/index.d.ts +2 -0
  100. package/dist/types/src/components/Transcription/index.d.ts.map +1 -0
  101. package/dist/types/src/components/Transcription/transcription-extension.d.ts +15 -0
  102. package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -0
  103. package/dist/types/src/components/index.d.ts +2 -3
  104. package/dist/types/src/components/index.d.ts.map +1 -1
  105. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts +6 -0
  106. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts.map +1 -0
  107. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts +28 -0
  108. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts.map +1 -0
  109. package/dist/types/src/containers/TranscriptionArticle/index.d.ts +2 -0
  110. package/dist/types/src/containers/TranscriptionArticle/index.d.ts.map +1 -0
  111. package/dist/types/src/containers/index.d.ts +3 -0
  112. package/dist/types/src/containers/index.d.ts.map +1 -0
  113. package/dist/types/src/enrichment/conversation.test.d.ts +2 -0
  114. package/dist/types/src/enrichment/conversation.test.d.ts.map +1 -0
  115. package/dist/types/src/enrichment/operations.d.ts +200 -0
  116. package/dist/types/src/enrichment/operations.d.ts.map +1 -0
  117. package/dist/types/src/enrichment/parser.d.ts +26 -0
  118. package/dist/types/src/enrichment/parser.d.ts.map +1 -0
  119. package/dist/types/src/enrichment/stream-simulator.d.ts +38 -0
  120. package/dist/types/src/enrichment/stream-simulator.d.ts.map +1 -0
  121. package/dist/types/src/hooks/index.d.ts +2 -1
  122. package/dist/types/src/hooks/index.d.ts.map +1 -1
  123. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  124. package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
  125. package/dist/types/src/hooks/useFeedModelAdapter.d.ts +12 -0
  126. package/dist/types/src/hooks/useFeedModelAdapter.d.ts.map +1 -0
  127. package/dist/types/src/hooks/useTranscriber.d.ts +2 -2
  128. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  129. package/dist/types/src/hooks/useTranscriptionRecording.d.ts +12 -0
  130. package/dist/types/src/hooks/useTranscriptionRecording.d.ts.map +1 -0
  131. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  132. package/dist/types/src/index.d.ts +2 -3
  133. package/dist/types/src/index.d.ts.map +1 -1
  134. package/dist/types/src/meta.d.ts +2 -3
  135. package/dist/types/src/meta.d.ts.map +1 -1
  136. package/dist/types/src/model/model.d.ts +2 -2
  137. package/dist/types/src/model/model.d.ts.map +1 -1
  138. package/dist/types/src/{segments-normalization → normalization}/index.d.ts +1 -1
  139. package/dist/types/src/normalization/index.d.ts.map +1 -0
  140. package/dist/types/src/{segments-normalization → normalization}/message-normalizer.d.ts +9 -7
  141. package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -0
  142. package/dist/types/src/normalization/normalization.d.ts +834 -0
  143. package/dist/types/src/normalization/normalization.d.ts.map +1 -0
  144. package/dist/types/src/normalization/normalization.test.d.ts.map +1 -0
  145. package/dist/types/src/normalization/utils.d.ts +3 -0
  146. package/dist/types/src/normalization/utils.d.ts.map +1 -0
  147. package/dist/types/src/operations/create.d.ts +5 -0
  148. package/dist/types/src/operations/create.d.ts.map +1 -0
  149. package/dist/types/src/operations/index.d.ts +3 -0
  150. package/dist/types/src/operations/index.d.ts.map +1 -0
  151. package/dist/types/src/operations/open.d.ts +5 -0
  152. package/dist/types/src/operations/open.d.ts.map +1 -0
  153. package/dist/types/src/operations/summarize.d.ts +8 -0
  154. package/dist/types/src/operations/summarize.d.ts.map +1 -0
  155. package/dist/types/src/plugin.d.ts +4 -0
  156. package/dist/types/src/plugin.d.ts.map +1 -0
  157. package/dist/types/src/stories/FileTranscription.stories.d.ts +20 -0
  158. package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -0
  159. package/dist/types/src/stories/KeyWordRecognition.stories.d.ts +14 -0
  160. package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -0
  161. package/dist/types/src/stories/LiveTranscription.stories.d.ts +20 -0
  162. package/dist/types/src/stories/LiveTranscription.stories.d.ts.map +1 -0
  163. package/dist/types/src/stories/TranscriptionStory.d.ts +21 -0
  164. package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -0
  165. package/dist/types/src/stories/common.d.ts +248 -0
  166. package/dist/types/src/stories/common.d.ts.map +1 -0
  167. package/dist/types/src/testing/testing.d.ts +14 -15
  168. package/dist/types/src/testing/testing.d.ts.map +1 -1
  169. package/dist/types/src/transcriber/index.d.ts +1 -1
  170. package/dist/types/src/transcriber/index.d.ts.map +1 -1
  171. package/dist/types/src/transcriber/media-stream-recorder.d.ts +2 -2
  172. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  173. package/dist/types/src/transcriber/transcriber.d.ts +61 -6
  174. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  175. package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
  176. package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
  177. package/dist/types/src/transcriber/transcription-manager.d.ts +17 -16
  178. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  179. package/dist/types/src/translations.d.ts +9 -8
  180. package/dist/types/src/translations.d.ts.map +1 -1
  181. package/dist/types/src/types/TranscriptOperation.d.ts +1670 -0
  182. package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
  183. package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
  184. package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
  185. package/dist/types/src/types/index.d.ts +2 -2
  186. package/dist/types/src/types/index.d.ts.map +1 -1
  187. package/dist/types/src/util/array.d.ts.map +1 -1
  188. package/dist/types/src/util/get-time-str.d.ts.map +1 -1
  189. package/dist/types/src/util/index.d.ts +1 -0
  190. package/dist/types/src/util/index.d.ts.map +1 -1
  191. package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
  192. package/dist/types/src/util/render-byline.d.ts +4 -0
  193. package/dist/types/src/util/render-byline.d.ts.map +1 -0
  194. package/dist/types/tsconfig.tsbuildinfo +1 -1
  195. package/package.json +153 -85
  196. package/src/TranscriptionPlugin.node.ts +20 -0
  197. package/src/TranscriptionPlugin.test.ts +31 -0
  198. package/src/TranscriptionPlugin.tsx +28 -61
  199. package/src/TranscriptionPlugin.workerd.ts +18 -0
  200. package/src/blueprints/index.ts +5 -0
  201. package/src/blueprints/transcription-blueprint.ts +29 -0
  202. package/src/capabilities/blueprint-definition.ts +19 -0
  203. package/src/capabilities/index.ts +16 -6
  204. package/src/capabilities/operation-handler.ts +16 -0
  205. package/src/capabilities/react-surface.tsx +23 -14
  206. package/src/capabilities/text-content.ts +33 -0
  207. package/src/capabilities/transcriber.ts +54 -41
  208. package/src/components/Oracle/Oracle.stories.tsx +147 -0
  209. package/src/components/Oracle/Oracle.tsx +142 -0
  210. package/src/components/Oracle/index.ts +5 -0
  211. package/src/components/Transcription/Transcription.stories.tsx +213 -0
  212. package/src/components/Transcription/Transcription.tsx +55 -0
  213. package/src/components/{Transcript → Transcription}/index.ts +1 -1
  214. package/src/components/{Transcript/transcript-extension.ts → Transcription/transcription-extension.ts} +13 -46
  215. package/src/components/index.ts +2 -6
  216. package/src/containers/TranscriptionArticle/TranscriptionArticle.stories.tsx +61 -0
  217. package/src/containers/TranscriptionArticle/TranscriptionArticle.tsx +62 -0
  218. package/src/containers/TranscriptionArticle/index.ts +5 -0
  219. package/src/containers/index.ts +7 -0
  220. package/src/enrichment/conversation.test.ts +162 -0
  221. package/src/enrichment/conversation.txt +23 -0
  222. package/src/enrichment/operations.ts +271 -0
  223. package/src/enrichment/parser.ts +101 -0
  224. package/src/enrichment/stream-simulator.ts +125 -0
  225. package/src/hooks/index.ts +2 -1
  226. package/src/hooks/useAudioFile.ts +49 -53
  227. package/src/hooks/useAudioTrack.ts +1 -1
  228. package/src/hooks/useFeedModelAdapter.ts +46 -0
  229. package/src/hooks/useTranscriber.ts +15 -7
  230. package/src/hooks/useTranscriptionRecording.ts +87 -0
  231. package/src/hooks/useVoiceInput.ts +3 -4
  232. package/src/index.ts +4 -5
  233. package/src/meta.ts +32 -7
  234. package/src/model/model.test.ts +22 -16
  235. package/src/model/model.ts +3 -3
  236. package/src/{segments-normalization → normalization}/message-normalizer.ts +37 -24
  237. package/src/{segments-normalization → normalization}/normalization.test.ts +9 -48
  238. package/src/normalization/normalization.ts +76 -0
  239. package/src/{segments-normalization → normalization}/utils.ts +2 -2
  240. package/src/operations/create.ts +24 -0
  241. package/src/operations/index.ts +11 -0
  242. package/src/operations/open.ts +28 -0
  243. package/src/operations/summarize.ts +102 -0
  244. package/src/plugin.ts +11 -0
  245. package/src/stories/FileTranscription.stories.tsx +143 -0
  246. package/src/{components/stories → stories}/KeyWordRecognition.stories.tsx +50 -46
  247. package/src/stories/LiveTranscription.stories.tsx +153 -0
  248. package/src/stories/TranscriptionStory.tsx +101 -0
  249. package/src/stories/common.ts +233 -0
  250. package/src/testing/testing.ts +53 -65
  251. package/src/transcriber/index.ts +1 -1
  252. package/src/transcriber/media-stream-recorder.ts +3 -3
  253. package/src/transcriber/transcriber.browser.test.ts +4 -5
  254. package/src/transcriber/transcriber.test.ts +104 -0
  255. package/src/transcriber/transcriber.ts +105 -60
  256. package/src/transcriber/transcription-manager.ts +49 -59
  257. package/src/translations.ts +10 -7
  258. package/src/types/TranscriptOperation.ts +106 -0
  259. package/src/types/TranscriptionCapabilities.ts +39 -0
  260. package/src/types/index.ts +2 -2
  261. package/src/util/index.ts +1 -0
  262. package/src/util/render-byline.ts +33 -0
  263. package/src/vite-env.d.ts +10 -0
  264. package/dist/lib/browser/TranscriptContainer-FH7VAWYA.mjs +0 -11
  265. package/dist/lib/browser/chunk-5WH45KBY.mjs +0 -19
  266. package/dist/lib/browser/chunk-5WH45KBY.mjs.map +0 -7
  267. package/dist/lib/browser/chunk-BJWYA2BU.mjs +0 -782
  268. package/dist/lib/browser/chunk-BJWYA2BU.mjs.map +0 -7
  269. package/dist/lib/browser/chunk-KJIQQHJB.mjs.map +0 -7
  270. package/dist/lib/browser/chunk-KWOOVS5G.mjs +0 -55
  271. package/dist/lib/browser/chunk-KWOOVS5G.mjs.map +0 -7
  272. package/dist/lib/browser/chunk-SREIAQSP.mjs +0 -16
  273. package/dist/lib/browser/chunk-SREIAQSP.mjs.map +0 -7
  274. package/dist/lib/browser/chunk-UKUSJTQW.mjs +0 -8
  275. package/dist/lib/browser/chunk-UKUSJTQW.mjs.map +0 -7
  276. package/dist/lib/browser/index.mjs +0 -125
  277. package/dist/lib/browser/index.mjs.map +0 -7
  278. package/dist/lib/browser/intent-resolver-AOVQ7JI4.mjs +0 -30
  279. package/dist/lib/browser/intent-resolver-AOVQ7JI4.mjs.map +0 -7
  280. package/dist/lib/browser/meta.json +0 -1
  281. package/dist/lib/browser/react-surface-4MEJMKYQ.mjs +0 -34
  282. package/dist/lib/browser/react-surface-4MEJMKYQ.mjs.map +0 -7
  283. package/dist/lib/browser/transcriber-V77CN7XX.mjs +0 -56
  284. package/dist/lib/browser/transcriber-V77CN7XX.mjs.map +0 -7
  285. package/dist/lib/browser/types/index.mjs +0 -14
  286. package/dist/lib/node-esm/TranscriptContainer-GEUOOYHX.mjs +0 -12
  287. package/dist/lib/node-esm/chunk-A33GNR44.mjs +0 -507
  288. package/dist/lib/node-esm/chunk-A33GNR44.mjs.map +0 -7
  289. package/dist/lib/node-esm/chunk-IKQMDRY6.mjs +0 -56
  290. package/dist/lib/node-esm/chunk-IKQMDRY6.mjs.map +0 -7
  291. package/dist/lib/node-esm/chunk-PHP7MCAY.mjs +0 -10
  292. package/dist/lib/node-esm/chunk-PHP7MCAY.mjs.map +0 -7
  293. package/dist/lib/node-esm/chunk-TM2ASOZA.mjs +0 -17
  294. package/dist/lib/node-esm/chunk-TM2ASOZA.mjs.map +0 -7
  295. package/dist/lib/node-esm/chunk-XMUCZBP3.mjs +0 -21
  296. package/dist/lib/node-esm/chunk-XMUCZBP3.mjs.map +0 -7
  297. package/dist/lib/node-esm/chunk-Y7PCJ34K.mjs +0 -783
  298. package/dist/lib/node-esm/chunk-Y7PCJ34K.mjs.map +0 -7
  299. package/dist/lib/node-esm/index.mjs +0 -126
  300. package/dist/lib/node-esm/index.mjs.map +0 -7
  301. package/dist/lib/node-esm/intent-resolver-P4XPIKQF.mjs +0 -31
  302. package/dist/lib/node-esm/intent-resolver-P4XPIKQF.mjs.map +0 -7
  303. package/dist/lib/node-esm/meta.json +0 -1
  304. package/dist/lib/node-esm/react-surface-EZUPNHUC.mjs +0 -35
  305. package/dist/lib/node-esm/react-surface-EZUPNHUC.mjs.map +0 -7
  306. package/dist/lib/node-esm/transcriber-3SX3DNLV.mjs +0 -57
  307. package/dist/lib/node-esm/transcriber-3SX3DNLV.mjs.map +0 -7
  308. package/dist/lib/node-esm/types/index.mjs +0 -15
  309. package/dist/types/src/capabilities/capabilities.d.ts +0 -17
  310. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  311. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  312. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  313. package/dist/types/src/components/Transcript/Transcript.d.ts +0 -20
  314. package/dist/types/src/components/Transcript/Transcript.d.ts.map +0 -1
  315. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +0 -24
  316. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +0 -1
  317. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +0 -28
  318. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +0 -1
  319. package/dist/types/src/components/Transcript/index.d.ts +0 -2
  320. package/dist/types/src/components/Transcript/index.d.ts.map +0 -1
  321. package/dist/types/src/components/Transcript/transcript-extension.d.ts +0 -19
  322. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +0 -1
  323. package/dist/types/src/components/TranscriptContainer.d.ts +0 -9
  324. package/dist/types/src/components/TranscriptContainer.d.ts.map +0 -1
  325. package/dist/types/src/components/stories/FileTranscription.stories.d.ts +0 -17
  326. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +0 -1
  327. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +0 -10
  328. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +0 -1
  329. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +0 -19
  330. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
  331. package/dist/types/src/components/stories/TranscriptionStory.d.ts +0 -11
  332. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +0 -1
  333. package/dist/types/src/components/stories/useIsSpeaking.d.ts +0 -3
  334. package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +0 -1
  335. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +0 -8
  336. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +0 -1
  337. package/dist/types/src/segments-normalization/index.d.ts.map +0 -1
  338. package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +0 -1
  339. package/dist/types/src/segments-normalization/normalization.d.ts +0 -422
  340. package/dist/types/src/segments-normalization/normalization.d.ts.map +0 -1
  341. package/dist/types/src/segments-normalization/normalization.test.d.ts.map +0 -1
  342. package/dist/types/src/segments-normalization/utils.d.ts +0 -3
  343. package/dist/types/src/segments-normalization/utils.d.ts.map +0 -1
  344. package/dist/types/src/types/schema.d.ts +0 -41
  345. package/dist/types/src/types/schema.d.ts.map +0 -1
  346. package/dist/types/src/types/types.d.ts +0 -31
  347. package/dist/types/src/types/types.d.ts.map +0 -1
  348. package/src/capabilities/capabilities.ts +0 -33
  349. package/src/capabilities/intent-resolver.ts +0 -24
  350. package/src/components/Transcript/Transcript.stories.tsx +0 -243
  351. package/src/components/Transcript/Transcript.tsx +0 -101
  352. package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
  353. package/src/components/TranscriptContainer.tsx +0 -36
  354. package/src/components/stories/FileTranscription.stories.tsx +0 -246
  355. package/src/components/stories/MicrophoneTranscription.stories.tsx +0 -283
  356. package/src/components/stories/TranscriptionStory.tsx +0 -41
  357. package/src/components/stories/useIsSpeaking.tsx +0 -40
  358. package/src/hooks/useQueueModelAdapter.ts +0 -66
  359. package/src/segments-normalization/normalization.ts +0 -115
  360. package/src/types/schema.ts +0 -41
  361. package/src/types/types.ts +0 -33
  362. package/dist/lib/{browser/types → neutral/blueprints}/index.mjs.map +0 -0
  363. package/dist/lib/{browser/TranscriptContainer-FH7VAWYA.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
  364. package/dist/lib/{node-esm/types → neutral/hooks}/index.mjs.map +0 -0
  365. package/dist/lib/{node-esm/TranscriptContainer-GEUOOYHX.mjs.map → neutral/index.mjs.map} +0 -0
  366. package/dist/types/src/{segments-normalization → normalization}/normalization.test.d.ts +0 -0
  367. package/src/{segments-normalization → normalization}/index.ts +1 -1
@@ -0,0 +1,271 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ // Operation definitions for the transcript enrichment pipeline (see ENRICHMENT.md).
6
+ //
7
+ // **Stub status.** The handlers below return deterministic dummy values rather than calling
8
+ // an LLM; they exist so the end-to-end test harness (`conversation.test.ts`) can exercise
9
+ // the schemas, the operation wiring, and the streaming/orchestration code paths without
10
+ // requiring an Anthropic key on first run. The real handlers will replace these stubs in
11
+ // a follow-up PR (calling `LanguageModel.generateObject(...)`) while keeping the same
12
+ // inputs / outputs.
13
+
14
+ import * as Effect from 'effect/Effect';
15
+ import * as Schema from 'effect/Schema';
16
+
17
+ import { Operation } from '@dxos/compute';
18
+ import { DXN } from '@dxos/keys';
19
+
20
+ import { meta } from '#meta';
21
+
22
+ const NAMESPACE = `${meta.id}.enrichment`;
23
+
24
+ //
25
+ // Shared sub-schemas.
26
+ //
27
+
28
+ /** A noun / proper noun surfaced for follow-up actions (create object, research, link…). */
29
+ export const CandidateMention = Schema.Struct({
30
+ text: Schema.String.annotations({ description: 'Surface form as it appears in the transcript.' }),
31
+ kind: Schema.Literal('noun', 'proper-noun').annotations({
32
+ description: 'Whether this is a common noun (e.g. "team") or a proper noun (e.g. "Munich").',
33
+ }),
34
+ start: Schema.Number.annotations({ description: 'Character offset within the corrected text.' }),
35
+ end: Schema.Number.annotations({ description: 'Character offset within the corrected text.' }),
36
+ suggested: Schema.optional(
37
+ Schema.Struct({
38
+ typename: Schema.String.annotations({
39
+ description: 'Optional hint for the ECHO type the user might create (e.g. "org.dxos.type.organization").',
40
+ }),
41
+ }),
42
+ ),
43
+ });
44
+
45
+ /** Per-block enrichment output produced by Pass A. */
46
+ export const EnrichmentBlock = Schema.Struct({
47
+ /** Index into the input window; lets the caller match enrichment to the originating block. */
48
+ index: Schema.Number,
49
+ /** Corrected text — punctuation, capitalization, words split across batch boundaries reassembled. */
50
+ corrected: Schema.String,
51
+ /**
52
+ * IDs of ECHO objects the LLM is confident the block mentions. Strings rather than `Ref<T>`
53
+ * to keep the schema serialisable; the consumer resolves them via `Database.resolve(...)`.
54
+ */
55
+ referenceIds: Schema.Array(Schema.String),
56
+ /** Candidate nouns / proper nouns not already covered by `referenceIds`. */
57
+ candidates: Schema.Array(CandidateMention),
58
+ });
59
+
60
+ //
61
+ // Pass A — enrichment (per Whisper batch / sliding window).
62
+ //
63
+
64
+ export const EnrichmentInput = Schema.Struct({
65
+ /** Sliding-window blocks. Each carries the speaker that produced it. */
66
+ window: Schema.Array(
67
+ Schema.Struct({
68
+ speaker: Schema.String,
69
+ text: Schema.String,
70
+ started: Schema.String,
71
+ }),
72
+ ),
73
+ /** ECHO objects available in the space that the LLM may pick from when linking references. */
74
+ knownEntities: Schema.optional(
75
+ Schema.Array(
76
+ Schema.Struct({
77
+ id: Schema.String,
78
+ typename: Schema.String,
79
+ name: Schema.String,
80
+ }),
81
+ ),
82
+ ),
83
+ });
84
+ export type EnrichmentInputType = Schema.Schema.Type<typeof EnrichmentInput>;
85
+
86
+ export const EnrichmentOutput = Schema.Struct({
87
+ blocks: Schema.Array(EnrichmentBlock),
88
+ });
89
+ export type EnrichmentOutputType = Schema.Schema.Type<typeof EnrichmentOutput>;
90
+
91
+ export const EnrichTranscript = Operation.make({
92
+ meta: {
93
+ key: DXN.make(`${NAMESPACE}.enrich`),
94
+ name: 'Enrich Transcript',
95
+ description: 'Correct Whisper output and surface entity references and candidates over a sliding window.',
96
+ icon: 'ph--microphone--regular',
97
+ },
98
+ input: EnrichmentInput,
99
+ output: EnrichmentOutput,
100
+ });
101
+
102
+ /**
103
+ * Deterministic stub: echoes the input text as `corrected`, links every block whose text contains
104
+ * a `knownEntities[i].name` substring to that entity's id, and surfaces every capitalised word
105
+ * not already linked as a `proper-noun` candidate. Sentence-end punctuation is added if missing.
106
+ */
107
+ const enrichHandler: Operation.WithHandler<typeof EnrichTranscript> = EnrichTranscript.pipe(
108
+ Operation.withHandler(
109
+ Effect.fnUntraced(function* ({ window, knownEntities }) {
110
+ const known = knownEntities ?? [];
111
+ const blocks = window.map((entry, index) => {
112
+ const corrected = ensureTerminalPunctuation(entry.text);
113
+ const referenceIds: string[] = [];
114
+ const covered: Array<{ start: number; end: number }> = [];
115
+ for (const entity of known) {
116
+ const haystack = corrected.toLowerCase();
117
+ const needle = entity.name.toLowerCase();
118
+ const idx = haystack.indexOf(needle);
119
+ if (idx >= 0) {
120
+ referenceIds.push(entity.id);
121
+ covered.push({ start: idx, end: idx + entity.name.length });
122
+ }
123
+ }
124
+
125
+ const candidates = [...findCapitalisedWords(corrected)].filter(
126
+ ({ start, end }) => !covered.some((c) => c.start <= start && end <= c.end),
127
+ );
128
+
129
+ return { index, corrected, referenceIds, candidates };
130
+ });
131
+ return { blocks };
132
+ }),
133
+ ),
134
+ );
135
+
136
+ //
137
+ // Pass B — summary (end-of-utterance + periodic).
138
+ //
139
+
140
+ export const SummaryInput = Schema.Struct({
141
+ /** The cumulative summary as of the last invocation (empty string on first call). */
142
+ previousSummary: Schema.String,
143
+ /** New corrected utterances appended since the previous summary. */
144
+ newUtterances: Schema.Array(
145
+ Schema.Struct({
146
+ speaker: Schema.String,
147
+ text: Schema.String,
148
+ }),
149
+ ),
150
+ /** Map of speaker label → human-readable display name. Drives "I" resolution. */
151
+ speakerNames: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
152
+ /** IDs and surface forms of references emitted by recent Pass A invocations. */
153
+ recentReferences: Schema.optional(
154
+ Schema.Array(
155
+ Schema.Struct({
156
+ id: Schema.String,
157
+ name: Schema.String,
158
+ }),
159
+ ),
160
+ ),
161
+ });
162
+ export type SummaryInputType = Schema.Schema.Type<typeof SummaryInput>;
163
+
164
+ export const ResolvedReferent = Schema.Struct({
165
+ surface: Schema.String.annotations({ description: 'The deictic / anaphoric form, e.g. "I", "there".' }),
166
+ referent: Schema.String.annotations({ description: 'Canonical name or description the surface resolves to.' }),
167
+ refId: Schema.optional(Schema.String).annotations({
168
+ description: 'ECHO object id when the referent maps to a known entity.',
169
+ }),
170
+ });
171
+
172
+ export const SummaryOutput = Schema.Struct({
173
+ summary: Schema.String,
174
+ resolvedReferents: Schema.Array(ResolvedReferent),
175
+ });
176
+ export type SummaryOutputType = Schema.Schema.Type<typeof SummaryOutput>;
177
+
178
+ export const SummarizeConversation = Operation.make({
179
+ meta: {
180
+ key: DXN.make(`${NAMESPACE}.summarize`),
181
+ name: 'Summarize Conversation',
182
+ description: 'Maintain a cumulative summary with referent resolution over a transcript stream.',
183
+ icon: 'ph--text-align-left--regular',
184
+ },
185
+ input: SummaryInput,
186
+ output: SummaryOutput,
187
+ });
188
+
189
+ /**
190
+ * Deterministic stub: appends each new utterance to the previous summary on its own line,
191
+ * prefixed with the speaker's display name (resolving the speaker label via `speakerNames`).
192
+ * If any utterance contains "I" as a standalone word, emits a `resolvedReferents` entry binding
193
+ * it to the speaker's display name — this is the minimal demonstration of the Pass-C contract.
194
+ */
195
+ const summarizeHandler: Operation.WithHandler<typeof SummarizeConversation> = SummarizeConversation.pipe(
196
+ Operation.withHandler(
197
+ Effect.fnUntraced(function* ({ previousSummary, newUtterances, speakerNames, recentReferences }) {
198
+ const names = speakerNames ?? {};
199
+ const lines = newUtterances.map((u) => `${names[u.speaker] ?? u.speaker}: ${u.text}`);
200
+ const summary = [previousSummary.trim(), ...lines].filter(Boolean).join('\n');
201
+
202
+ const resolvedReferents = newUtterances
203
+ .filter((u) => /\bI\b/.test(u.text))
204
+ .map((u) => {
205
+ const displayName = names[u.speaker] ?? u.speaker;
206
+ const ref = (recentReferences ?? []).find((r) => r.name === displayName);
207
+ return { surface: 'I', referent: displayName, refId: ref?.id };
208
+ });
209
+
210
+ return { summary, resolvedReferents };
211
+ }),
212
+ ),
213
+ );
214
+
215
+ //
216
+ // Exports.
217
+ //
218
+
219
+ export const EnrichmentHandlers = [enrichHandler, summarizeHandler];
220
+
221
+ //
222
+ // Helpers.
223
+ //
224
+
225
+ const ensureTerminalPunctuation = (text: string): string => {
226
+ const trimmed = text.trim();
227
+ if (trimmed.length === 0) {
228
+ return trimmed;
229
+ }
230
+ return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
231
+ };
232
+
233
+ function* findCapitalisedWords(
234
+ text: string,
235
+ ): Generator<{ text: string; kind: 'proper-noun'; start: number; end: number }> {
236
+ const re = /\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/g;
237
+ let match: RegExpExecArray | null;
238
+ while ((match = re.exec(text)) !== null) {
239
+ // Skip pure sentence-initial single words ("Yes", "I", "But", "They") to keep candidates focused on
240
+ // proper nouns. Heuristic for the stub: drop common stop-words.
241
+ const word = match[0];
242
+ if (STOP_WORDS.has(word)) {
243
+ continue;
244
+ }
245
+ yield { text: word, kind: 'proper-noun', start: match.index, end: match.index + word.length };
246
+ }
247
+ }
248
+
249
+ const STOP_WORDS = new Set([
250
+ 'I',
251
+ 'Yes',
252
+ 'No',
253
+ 'But',
254
+ 'And',
255
+ 'Or',
256
+ 'So',
257
+ 'They',
258
+ 'We',
259
+ 'He',
260
+ 'She',
261
+ 'It',
262
+ 'The',
263
+ 'A',
264
+ 'An',
265
+ 'Is',
266
+ 'Are',
267
+ 'Was',
268
+ 'Were',
269
+ 'Be',
270
+ 'Been',
271
+ ]);
@@ -0,0 +1,101 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ // Parser for the human-authored conversation fixture (see `conversation.txt`).
6
+ // Captures both the seed entities (Persons, Organizations, …) and the speaker-tagged
7
+ // dialogue used by `stream-simulator.ts` to feed the enrichment pipeline.
8
+
9
+ export type ConversationEntity = {
10
+ /** Type name as written in the fixture, e.g. `Person`, `Organization`. */
11
+ kind: string;
12
+ /** Free-form field values; consumers map these to the relevant ECHO schema. */
13
+ fields: Record<string, string>;
14
+ };
15
+
16
+ export type ConversationUtterance = {
17
+ /** Speaker label as written in the fixture (e.g. `A`, `B`, `Rich`). */
18
+ speaker: string;
19
+ /** Raw utterance text. */
20
+ text: string;
21
+ };
22
+
23
+ export type ParsedConversation = {
24
+ entities: ConversationEntity[];
25
+ utterances: ConversationUtterance[];
26
+ /** Distinct speaker labels in first-seen order. */
27
+ speakers: string[];
28
+ };
29
+
30
+ /**
31
+ * Parse the on-disk conversation format into structured data.
32
+ *
33
+ * Tolerates leading whitespace inside blocks and ignores blank / `#` comment lines.
34
+ * Throws if the input doesn't contain a `Conversation:` block — `Entities:` is optional.
35
+ */
36
+ export const parseConversation = (input: string): ParsedConversation => {
37
+ const lines = input.split('\n').map((line) => line.replace(/\s+$/, ''));
38
+
39
+ const entities: ConversationEntity[] = [];
40
+ const utterances: ConversationUtterance[] = [];
41
+ const speakerOrder = new Set<string>();
42
+
43
+ type Section = 'none' | 'entities' | 'conversation';
44
+ let section: Section = 'none';
45
+ let currentEntity: ConversationEntity | undefined;
46
+
47
+ for (const raw of lines) {
48
+ const line = raw.trim();
49
+ if (line.length === 0 || line.startsWith('#')) {
50
+ continue;
51
+ }
52
+
53
+ // Section headers (must be left-aligned to switch sections).
54
+ if (raw.startsWith('Entities:') || raw.startsWith('Entitites:')) {
55
+ // tolerate the typo present in earlier fixtures
56
+ section = 'entities';
57
+ currentEntity = undefined;
58
+ continue;
59
+ }
60
+ if (raw.startsWith('Conversation:')) {
61
+ section = 'conversation';
62
+ currentEntity = undefined;
63
+ continue;
64
+ }
65
+
66
+ if (section === 'entities') {
67
+ // `<TypeName>:` starts a new entity; nested `<field>: <value>` lines fill it.
68
+ const typeMatch = line.match(/^([A-Za-z][A-Za-z0-9_-]*):$/);
69
+ if (typeMatch) {
70
+ currentEntity = { kind: typeMatch[1], fields: {} };
71
+ entities.push(currentEntity);
72
+ continue;
73
+ }
74
+ const fieldMatch = line.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.+)$/);
75
+ if (fieldMatch && currentEntity) {
76
+ currentEntity.fields[fieldMatch[1]] = fieldMatch[2];
77
+ continue;
78
+ }
79
+ // Silently skip unrecognised lines so authors can add free-form annotations.
80
+ continue;
81
+ }
82
+
83
+ if (section === 'conversation') {
84
+ const utteranceMatch = line.match(/^([^:]+):\s*(.+)$/);
85
+ if (utteranceMatch) {
86
+ const speaker = utteranceMatch[1].trim();
87
+ const text = utteranceMatch[2].trim();
88
+ if (text.length > 0) {
89
+ utterances.push({ speaker, text });
90
+ speakerOrder.add(speaker);
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ if (utterances.length === 0) {
97
+ throw new Error('parseConversation: no utterances found');
98
+ }
99
+
100
+ return { entities, utterances, speakers: Array.from(speakerOrder) };
101
+ };
@@ -0,0 +1,125 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ // Turns a parsed conversation into a chronologically-ordered sequence of Whisper-shaped
6
+ // transcript batches, one per (speaker, utterance) pair. Each batch carries a virtual
7
+ // timestamp (ms since the conversation start) and the speaker label so consumers can fan
8
+ // out per-stream — analogous to two microphones being transcribed in parallel.
9
+ //
10
+ // The simulator does NOT do any wall-clock waiting. Tests drive virtual time via
11
+ // `vi.useFakeTimers()` or step through the batches directly; production code wouldn't use
12
+ // this module at all.
13
+
14
+ import { type ContentBlock } from '@dxos/types';
15
+
16
+ import type { ConversationUtterance, ParsedConversation } from './parser';
17
+
18
+ export type StreamSimulatorOptions = {
19
+ /**
20
+ * Average speaking rate, words per second. Used to assign a virtual duration to each
21
+ * utterance so consecutive utterances land at realistic offsets.
22
+ * @default 2.5 (≈150 wpm — typical conversational pace)
23
+ */
24
+ wordsPerSecond?: number;
25
+
26
+ /**
27
+ * Gap between consecutive utterances in seconds. Models a brief pause for turn-taking
28
+ * even when speakers don't actually overlap.
29
+ * @default 0.5
30
+ */
31
+ gapSeconds?: number;
32
+
33
+ /**
34
+ * Whisper batches typically arrive in chunks of N seconds rather than per-utterance.
35
+ * When set, utterances shorter than `batchSeconds` are merged into the same batch as
36
+ * the following utterance from the same speaker. `0` disables merging — every utterance
37
+ * is its own batch.
38
+ * @default 0
39
+ */
40
+ batchSeconds?: number;
41
+ };
42
+
43
+ export type SimulatedBatch = {
44
+ /** Speaker label from the fixture (`A`, `B`, …). Maps to a virtual `identityDid`. */
45
+ speaker: string;
46
+ /** ms offset from the conversation start when this batch is delivered. */
47
+ offsetMs: number;
48
+ /** Whisper-shaped transcript blocks (raw text, no enrichment). */
49
+ blocks: ContentBlock.Transcript[];
50
+ };
51
+
52
+ /**
53
+ * Build the chronological batch schedule for a parsed conversation. The result is a
54
+ * pure data structure — consumers iterate and drive virtual time themselves.
55
+ */
56
+ export const simulateStream = (
57
+ conversation: ParsedConversation,
58
+ options: StreamSimulatorOptions = {},
59
+ ): SimulatedBatch[] => {
60
+ const wordsPerSecond = options.wordsPerSecond ?? 2.5;
61
+ const gapSeconds = options.gapSeconds ?? 0.5;
62
+ const batchSeconds = options.batchSeconds ?? 0;
63
+
64
+ // Guard against divide-by-zero / NaN / negative inputs — `wordsPerSecond` divides into a
65
+ // duration below, and the other knobs feed into offsets that must be finite.
66
+ if (!Number.isFinite(wordsPerSecond) || wordsPerSecond <= 0) {
67
+ throw new Error(`simulateStream: wordsPerSecond must be a finite number > 0 (got ${wordsPerSecond}).`);
68
+ }
69
+ if (!Number.isFinite(gapSeconds) || gapSeconds < 0) {
70
+ throw new Error(`simulateStream: gapSeconds must be a finite number >= 0 (got ${gapSeconds}).`);
71
+ }
72
+ if (!Number.isFinite(batchSeconds) || batchSeconds < 0) {
73
+ throw new Error(`simulateStream: batchSeconds must be a finite number >= 0 (got ${batchSeconds}).`);
74
+ }
75
+
76
+ const conversationStart = Date.UTC(2024, 0, 1, 12, 0, 0); // deterministic, ignored by callers
77
+ let cursorMs = 0;
78
+ const batches: SimulatedBatch[] = [];
79
+
80
+ // Optionally merge same-speaker consecutive utterances until they reach `batchSeconds`.
81
+ const grouped: { speaker: string; utterances: ConversationUtterance[] }[] = [];
82
+ if (batchSeconds === 0) {
83
+ for (const u of conversation.utterances) {
84
+ grouped.push({ speaker: u.speaker, utterances: [u] });
85
+ }
86
+ } else {
87
+ let pending: { speaker: string; utterances: ConversationUtterance[]; words: number } | undefined;
88
+ for (const u of conversation.utterances) {
89
+ const words = utteranceWordCount(u.text);
90
+ if (pending && pending.speaker === u.speaker && pending.words / wordsPerSecond < batchSeconds) {
91
+ pending.utterances.push(u);
92
+ pending.words += words;
93
+ } else {
94
+ if (pending) {
95
+ grouped.push({ speaker: pending.speaker, utterances: pending.utterances });
96
+ }
97
+ pending = { speaker: u.speaker, utterances: [u], words };
98
+ }
99
+ }
100
+ if (pending) {
101
+ grouped.push({ speaker: pending.speaker, utterances: pending.utterances });
102
+ }
103
+ }
104
+
105
+ for (const group of grouped) {
106
+ const startMs = cursorMs;
107
+ const blocks: ContentBlock.Transcript[] = [];
108
+ let segmentCursorMs = cursorMs;
109
+ for (const u of group.utterances) {
110
+ const durationMs = Math.max(500, (utteranceWordCount(u.text) / wordsPerSecond) * 1_000);
111
+ blocks.push({
112
+ _tag: 'transcript',
113
+ started: new Date(conversationStart + segmentCursorMs).toISOString(),
114
+ text: u.text,
115
+ });
116
+ segmentCursorMs += durationMs;
117
+ }
118
+ batches.push({ speaker: group.speaker, offsetMs: startMs, blocks });
119
+ cursorMs = segmentCursorMs + gapSeconds * 1_000;
120
+ }
121
+
122
+ return batches;
123
+ };
124
+
125
+ const utteranceWordCount = (text: string): number => text.trim().split(/\s+/).filter(Boolean).length;
@@ -4,6 +4,7 @@
4
4
 
5
5
  export * from './useAudioFile';
6
6
  export * from './useAudioTrack';
7
- export * from './useQueueModelAdapter';
7
+ export * from './useFeedModelAdapter';
8
8
  export * from './useTranscriber';
9
+ export * from './useTranscriptionRecording';
9
10
  export * from './useVoiceInput';
@@ -20,67 +20,63 @@ export const useAudioFile = (audioUrl: string, constraints?: MediaTrackConstrain
20
20
  const ctx = new Context();
21
21
 
22
22
  scheduleTask(ctx, async () => {
23
- const response = await fetch(audioUrl);
24
- const blob = await response.blob();
25
- const objectUrl = URL.createObjectURL(blob);
26
- ctx.onDispose(() => {
27
- URL.revokeObjectURL(objectUrl);
28
- });
23
+ // TODO(wittjosiah): Fetch to external url fails in headless storybook test.
24
+ try {
25
+ const response = await fetch(audioUrl);
26
+ const blob = await response.blob();
27
+ const objectUrl = URL.createObjectURL(blob);
28
+ ctx.onDispose(() => {
29
+ URL.revokeObjectURL(objectUrl);
30
+ });
29
31
 
30
- const audio = new Audio();
31
- audio.src = objectUrl;
32
+ const audio = new Audio();
33
+ audio.src = objectUrl;
32
34
 
33
- await new Promise<void>((resolve, reject) => {
34
- audio.addEventListener(
35
- 'error',
36
- (err) => {
37
- log.error('error', { err });
38
- reject(err);
39
- },
40
- { once: true },
41
- );
42
- audio.addEventListener(
43
- 'canplay',
44
- async () => {
45
- log.info('starting...');
46
- try {
47
- // Try to play the audio
48
- await audio.play();
35
+ await new Promise<void>((resolve, reject) => {
36
+ audio.addEventListener(
37
+ 'error',
38
+ (err) => {
39
+ log.error('error', { err });
40
+ reject(err);
41
+ },
42
+ { once: true },
43
+ );
44
+ audio.addEventListener(
45
+ 'canplay',
46
+ async () => {
49
47
  resolve();
50
- } catch (playError) {
51
- log.error('Play failed', { playError });
52
- // Still resolve as the audio is ready, even if autoplay failed.
53
- resolve();
54
- }
55
- },
56
- { once: true },
57
- );
58
- audio.load();
59
- });
48
+ },
49
+ { once: true },
50
+ );
51
+ audio.load();
52
+ });
60
53
 
61
- const audioCtx = new AudioContext();
54
+ const audioCtx = new AudioContext();
62
55
 
63
- // Resume AudioContext if it's suspended.
64
- if (audioCtx.state === 'suspended') {
65
- await audioCtx.resume();
66
- }
56
+ // Resume AudioContext if it's suspended.
57
+ if (audioCtx.state === 'suspended') {
58
+ await audioCtx.resume();
59
+ }
67
60
 
68
- const destination = audioCtx.createMediaStreamDestination();
69
- destination.channelCount = 1;
61
+ const destination = audioCtx.createMediaStreamDestination();
62
+ destination.channelCount = 1;
70
63
 
71
- const source = audioCtx.createMediaElementSource(audio);
72
- source.connect(destination);
73
- const track = destination.stream.getAudioTracks()[0];
74
- if (constraints) {
75
- await track.applyConstraints(constraints).catch((err) => log.catch(err));
76
- }
64
+ const source = audioCtx.createMediaElementSource(audio);
65
+ source.connect(destination);
66
+ const track = destination.stream.getAudioTracks()[0];
67
+ if (constraints) {
68
+ await track.applyConstraints(constraints).catch((err) => log.catch(err));
69
+ }
77
70
 
78
- // Also connect to speakers so audio is audible.
79
- setStream({
80
- audio,
81
- stream: destination.stream,
82
- track: destination.stream.getAudioTracks()[0],
83
- });
71
+ // Also connect to speakers so audio is audible.
72
+ setStream({
73
+ audio,
74
+ stream: destination.stream,
75
+ track: destination.stream.getAudioTracks()[0],
76
+ });
77
+ } catch (err) {
78
+ log.catch(err);
79
+ }
84
80
  });
85
81
 
86
82
  return () => {
@@ -7,7 +7,7 @@ import { useEffect, useState } from 'react';
7
7
  /**
8
8
  *
9
9
  */
10
- // TODO(burdon): Reconcile with react-ui-sfx and plugin-calls.
10
+ // TODO(burdon): Reconcile with react-ui-experimental and plugin-calls.
11
11
  export const useAudioTrack = (active?: boolean, constraints?: MediaTrackConstraints): MediaStreamTrack | undefined => {
12
12
  const [track, setTrack] = useState<MediaStreamTrack>();
13
13
  useEffect(() => {
@@ -0,0 +1,46 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useEffect, useMemo } from 'react';
6
+
7
+ import type { Obj } from '@dxos/echo';
8
+
9
+ import { type ChunkRenderer, SerializationModel } from '../model';
10
+
11
+ /**
12
+ * Feeds an array of objects (typically from `useQuery(db, Query.from(feed))`) into a
13
+ * `SerializationModel`, appending each new tail item as the array grows.
14
+ *
15
+ * @param renderer - chunk renderer.
16
+ * @param objects - current snapshot of feed items.
17
+ * @param initialChunks - chunks to seed the model with on first render.
18
+ */
19
+ export const useFeedModelAdapter = <T extends Obj.Unknown>(
20
+ renderer: ChunkRenderer<T>,
21
+ objects: readonly T[],
22
+ initialChunks: T[] = [],
23
+ ): SerializationModel<T> => {
24
+ const model = useMemo(() => new SerializationModel<T>(renderer, initialChunks), [renderer]);
25
+
26
+ // Seed the model with the current snapshot on first render and whenever the
27
+ // identity of `objects` changes (e.g. feed swapped).
28
+ useEffect(() => {
29
+ for (const chunk of objects) {
30
+ model.appendChunk(chunk);
31
+ }
32
+ }, [model, objects]);
33
+
34
+ // Append new tail items as the array grows.
35
+ useEffect(() => {
36
+ if (model.chunks.length === objects.length) {
37
+ return;
38
+ }
39
+ const chunk = objects.at(-1);
40
+ if (chunk) {
41
+ model.appendChunk(chunk);
42
+ }
43
+ }, [model, objects.length]);
44
+
45
+ return model;
46
+ };