@dxos/plugin-transcription 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc

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 (262) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs +54 -0
  4. package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs.map +7 -0
  5. package/dist/lib/neutral/TranscriptionPlugin.mjs +34 -0
  6. package/dist/lib/neutral/TranscriptionPlugin.mjs.map +7 -0
  7. package/dist/lib/neutral/TranscriptionPlugin.node.mjs +25 -0
  8. package/dist/lib/neutral/TranscriptionPlugin.node.mjs.map +7 -0
  9. package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs +21 -0
  10. package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs.map +7 -0
  11. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs +15 -0
  12. package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs.map +7 -0
  13. package/dist/lib/neutral/blueprints/index.mjs +8 -0
  14. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  15. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  16. package/dist/lib/{browser/chunk-NQQK7UZQ.mjs → neutral/chunk-2O3ONPRZ.mjs} +94 -40
  17. package/dist/lib/neutral/chunk-2O3ONPRZ.mjs.map +7 -0
  18. package/dist/lib/{browser/chunk-L4HW35L6.mjs → neutral/chunk-5FXU36PN.mjs} +19 -10
  19. package/dist/lib/neutral/chunk-5FXU36PN.mjs.map +7 -0
  20. package/dist/lib/neutral/chunk-GL4RS533.mjs +22 -0
  21. package/dist/lib/neutral/chunk-GL4RS533.mjs.map +7 -0
  22. package/dist/lib/{browser/index.mjs → neutral/chunk-ILSA3DU7.mjs} +32 -70
  23. package/dist/lib/neutral/chunk-ILSA3DU7.mjs.map +7 -0
  24. package/dist/lib/neutral/chunk-Y5EQAFFR.mjs +8 -0
  25. package/dist/lib/neutral/chunk-Y5EQAFFR.mjs.map +7 -0
  26. package/dist/lib/{browser/blueprints/index.mjs → neutral/chunk-ZR4BH4QQ.mjs} +4 -5
  27. package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
  28. package/dist/lib/{browser → neutral}/components/index.mjs +83 -18
  29. package/dist/lib/neutral/components/index.mjs.map +7 -0
  30. package/dist/lib/neutral/containers/index.mjs +9 -0
  31. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  32. package/dist/lib/{browser/create-ULR5DPJZ.mjs → neutral/create-RRWRIUSR.mjs} +4 -4
  33. package/dist/lib/neutral/create-RRWRIUSR.mjs.map +7 -0
  34. package/dist/lib/{browser → neutral}/hooks/index.mjs +5 -3
  35. package/dist/lib/neutral/index.mjs +44 -0
  36. package/dist/lib/neutral/meta.json +1 -0
  37. package/dist/lib/neutral/meta.mjs +8 -0
  38. package/dist/lib/{browser/open-SFMPLZVW.mjs → neutral/open-CVMX5KXZ.mjs} +5 -5
  39. package/dist/lib/neutral/open-CVMX5KXZ.mjs.map +7 -0
  40. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
  41. package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
  42. package/dist/lib/neutral/operations/index.mjs +8 -0
  43. package/dist/lib/neutral/plugin.mjs +16 -0
  44. package/dist/lib/neutral/plugin.mjs.map +7 -0
  45. package/dist/lib/neutral/react-surface-YMZRO242.mjs +26 -0
  46. package/dist/lib/neutral/react-surface-YMZRO242.mjs.map +7 -0
  47. package/dist/lib/{browser/summarize-BDFX4APN.mjs → neutral/summarize-LA7TRW7A.mjs} +5 -5
  48. package/dist/lib/neutral/summarize-LA7TRW7A.mjs.map +7 -0
  49. package/dist/lib/neutral/text-content-F4ASMITB.mjs +30 -0
  50. package/dist/lib/neutral/text-content-F4ASMITB.mjs.map +7 -0
  51. package/dist/lib/neutral/transcriber-ABHATBVH.mjs +59 -0
  52. package/dist/lib/neutral/transcriber-ABHATBVH.mjs.map +7 -0
  53. package/dist/lib/neutral/translations.mjs +24 -0
  54. package/dist/lib/neutral/translations.mjs.map +7 -0
  55. package/dist/lib/neutral/types/index.mjs +10 -0
  56. package/dist/lib/neutral/types/index.mjs.map +7 -0
  57. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  58. package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -1
  59. package/dist/types/src/TranscriptionPlugin.workerd.d.ts +4 -0
  60. package/dist/types/src/TranscriptionPlugin.workerd.d.ts.map +1 -0
  61. package/dist/types/src/capabilities/index.d.ts +5 -1
  62. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  63. package/dist/types/src/capabilities/text-content.d.ts +8 -0
  64. package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
  65. package/dist/types/src/components/Oracle/Oracle.d.ts +24 -0
  66. package/dist/types/src/components/Oracle/Oracle.d.ts.map +1 -0
  67. package/dist/types/src/components/Oracle/Oracle.stories.d.ts +32 -0
  68. package/dist/types/src/components/Oracle/Oracle.stories.d.ts.map +1 -0
  69. package/dist/types/src/components/Oracle/index.d.ts +2 -0
  70. package/dist/types/src/components/Oracle/index.d.ts.map +1 -0
  71. package/dist/types/src/components/Transcription/Transcription.d.ts +0 -5
  72. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -1
  73. package/dist/types/src/components/Transcription/Transcription.stories.d.ts +13 -16
  74. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -1
  75. package/dist/types/src/components/index.d.ts +1 -0
  76. package/dist/types/src/components/index.d.ts.map +1 -1
  77. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts +6 -0
  78. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts.map +1 -0
  79. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts +28 -0
  80. package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts.map +1 -0
  81. package/dist/types/src/containers/TranscriptionArticle/index.d.ts +2 -0
  82. package/dist/types/src/containers/TranscriptionArticle/index.d.ts.map +1 -0
  83. package/dist/types/src/containers/index.d.ts +1 -1
  84. package/dist/types/src/containers/index.d.ts.map +1 -1
  85. package/dist/types/src/enrichment/conversation.test.d.ts +2 -0
  86. package/dist/types/src/enrichment/conversation.test.d.ts.map +1 -0
  87. package/dist/types/src/enrichment/operations.d.ts +200 -0
  88. package/dist/types/src/enrichment/operations.d.ts.map +1 -0
  89. package/dist/types/src/enrichment/parser.d.ts +26 -0
  90. package/dist/types/src/enrichment/parser.d.ts.map +1 -0
  91. package/dist/types/src/enrichment/stream-simulator.d.ts +38 -0
  92. package/dist/types/src/enrichment/stream-simulator.d.ts.map +1 -0
  93. package/dist/types/src/hooks/index.d.ts +2 -1
  94. package/dist/types/src/hooks/index.d.ts.map +1 -1
  95. package/dist/types/src/hooks/useFeedModelAdapter.d.ts +12 -0
  96. package/dist/types/src/hooks/useFeedModelAdapter.d.ts.map +1 -0
  97. package/dist/types/src/hooks/useTranscriptionRecording.d.ts +12 -0
  98. package/dist/types/src/hooks/useTranscriptionRecording.d.ts.map +1 -0
  99. package/dist/types/src/index.d.ts +3 -4
  100. package/dist/types/src/index.d.ts.map +1 -1
  101. package/dist/types/src/normalization/message-normalizer.d.ts +7 -5
  102. package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -1
  103. package/dist/types/src/normalization/normalization.d.ts +180 -1
  104. package/dist/types/src/normalization/normalization.d.ts.map +1 -1
  105. package/dist/types/src/operations/create.d.ts +2 -2
  106. package/dist/types/src/operations/create.d.ts.map +1 -1
  107. package/dist/types/src/operations/index.d.ts +0 -1
  108. package/dist/types/src/operations/index.d.ts.map +1 -1
  109. package/dist/types/src/operations/open.d.ts +2 -2
  110. package/dist/types/src/operations/open.d.ts.map +1 -1
  111. package/dist/types/src/operations/summarize.d.ts +2 -2
  112. package/dist/types/src/operations/summarize.d.ts.map +1 -1
  113. package/dist/types/src/plugin.d.ts +4 -0
  114. package/dist/types/src/plugin.d.ts.map +1 -0
  115. package/dist/types/src/stories/FileTranscription.stories.d.ts +5 -3
  116. package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -1
  117. package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  118. package/dist/types/src/stories/{MicrophoneTranscription.stories.d.ts → LiveTranscription.stories.d.ts} +4 -4
  119. package/dist/types/src/stories/LiveTranscription.stories.d.ts.map +1 -0
  120. package/dist/types/src/stories/TranscriptionStory.d.ts +11 -13
  121. package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -1
  122. package/dist/types/src/stories/common.d.ts +46 -0
  123. package/dist/types/src/stories/common.d.ts.map +1 -0
  124. package/dist/types/src/testing/testing.d.ts +5 -6
  125. package/dist/types/src/testing/testing.d.ts.map +1 -1
  126. package/dist/types/src/transcriber/transcriber.d.ts +4 -4
  127. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  128. package/dist/types/src/transcriber/transcription-manager.d.ts +6 -4
  129. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  130. package/dist/types/src/translations.d.ts +2 -0
  131. package/dist/types/src/translations.d.ts.map +1 -1
  132. package/dist/types/src/{operations/definitions.d.ts → types/TranscriptOperation.d.ts} +1 -1
  133. package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
  134. package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
  135. package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
  136. package/dist/types/src/types/index.d.ts +2 -2
  137. package/dist/types/src/types/index.d.ts.map +1 -1
  138. package/dist/types/tsconfig.tsbuildinfo +1 -1
  139. package/package.json +125 -94
  140. package/src/TranscriptionPlugin.node.ts +3 -31
  141. package/src/TranscriptionPlugin.test.ts +3 -3
  142. package/src/TranscriptionPlugin.tsx +3 -31
  143. package/src/TranscriptionPlugin.workerd.ts +18 -0
  144. package/src/blueprints/transcription-blueprint.ts +1 -1
  145. package/src/capabilities/index.ts +1 -0
  146. package/src/capabilities/react-surface.tsx +2 -2
  147. package/src/capabilities/text-content.ts +33 -0
  148. package/src/capabilities/transcriber.ts +1 -1
  149. package/src/components/Oracle/Oracle.stories.tsx +147 -0
  150. package/src/components/Oracle/Oracle.tsx +141 -0
  151. package/src/components/Oracle/index.ts +5 -0
  152. package/src/components/Transcription/Transcription.stories.tsx +116 -150
  153. package/src/components/Transcription/Transcription.tsx +7 -13
  154. package/src/components/index.ts +1 -0
  155. package/src/containers/TranscriptionArticle/TranscriptionArticle.stories.tsx +61 -0
  156. package/src/containers/TranscriptionArticle/TranscriptionArticle.tsx +62 -0
  157. package/src/containers/TranscriptionArticle/index.ts +5 -0
  158. package/src/containers/index.ts +1 -1
  159. package/src/enrichment/conversation.test.ts +162 -0
  160. package/src/enrichment/conversation.txt +23 -0
  161. package/src/enrichment/operations.ts +268 -0
  162. package/src/enrichment/parser.ts +101 -0
  163. package/src/enrichment/stream-simulator.ts +125 -0
  164. package/src/hooks/index.ts +2 -1
  165. package/src/hooks/useFeedModelAdapter.ts +46 -0
  166. package/src/hooks/useTranscriptionRecording.ts +87 -0
  167. package/src/index.ts +3 -9
  168. package/src/normalization/message-normalizer.ts +25 -13
  169. package/src/normalization/normalization.test.ts +3 -37
  170. package/src/normalization/normalization.ts +6 -7
  171. package/src/operations/create.ts +2 -2
  172. package/src/operations/index.ts +0 -2
  173. package/src/operations/open.ts +2 -2
  174. package/src/operations/summarize.ts +3 -3
  175. package/src/plugin.ts +11 -0
  176. package/src/stories/FileTranscription.stories.tsx +49 -175
  177. package/src/stories/KeyWordRecognition.stories.tsx +7 -9
  178. package/src/stories/LiveTranscription.stories.tsx +153 -0
  179. package/src/stories/TranscriptionStory.tsx +32 -24
  180. package/src/stories/common.ts +233 -0
  181. package/src/testing/testing.ts +20 -27
  182. package/src/transcriber/transcriber.test.ts +9 -9
  183. package/src/transcriber/transcriber.ts +18 -14
  184. package/src/transcriber/transcription-manager.ts +14 -9
  185. package/src/translations.ts +3 -0
  186. package/src/{operations/definitions.ts → types/TranscriptOperation.ts} +2 -0
  187. package/src/types/TranscriptionCapabilities.ts +39 -0
  188. package/src/types/index.ts +2 -2
  189. package/dist/lib/browser/blueprints/index.mjs.map +0 -7
  190. package/dist/lib/browser/chunk-3L623EVL.mjs +0 -17
  191. package/dist/lib/browser/chunk-3L623EVL.mjs.map +0 -7
  192. package/dist/lib/browser/chunk-L4HW35L6.mjs.map +0 -7
  193. package/dist/lib/browser/chunk-NQQK7UZQ.mjs.map +0 -7
  194. package/dist/lib/browser/components/index.mjs.map +0 -7
  195. package/dist/lib/browser/create-ULR5DPJZ.mjs.map +0 -7
  196. package/dist/lib/browser/index.mjs.map +0 -7
  197. package/dist/lib/browser/meta.json +0 -1
  198. package/dist/lib/browser/open-SFMPLZVW.mjs.map +0 -7
  199. package/dist/lib/browser/operations/index.mjs +0 -13
  200. package/dist/lib/browser/operations/index.mjs.map +0 -7
  201. package/dist/lib/browser/summarize-BDFX4APN.mjs.map +0 -7
  202. package/dist/lib/browser/types/index.mjs +0 -10
  203. package/dist/lib/node-esm/blueprints/index.mjs +0 -32
  204. package/dist/lib/node-esm/blueprints/index.mjs.map +0 -7
  205. package/dist/lib/node-esm/chunk-7UIZWCD4.mjs +0 -112
  206. package/dist/lib/node-esm/chunk-7UIZWCD4.mjs.map +0 -7
  207. package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs +0 -181
  208. package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs.map +0 -7
  209. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  210. package/dist/lib/node-esm/chunk-KO3IOD2O.mjs +0 -19
  211. package/dist/lib/node-esm/chunk-KO3IOD2O.mjs.map +0 -7
  212. package/dist/lib/node-esm/chunk-MQK4R6GT.mjs +0 -245
  213. package/dist/lib/node-esm/chunk-MQK4R6GT.mjs.map +0 -7
  214. package/dist/lib/node-esm/chunk-O533NBMK.mjs +0 -37
  215. package/dist/lib/node-esm/chunk-O533NBMK.mjs.map +0 -7
  216. package/dist/lib/node-esm/components/index.mjs +0 -208
  217. package/dist/lib/node-esm/components/index.mjs.map +0 -7
  218. package/dist/lib/node-esm/create-A5QHNWRF.mjs +0 -22
  219. package/dist/lib/node-esm/create-A5QHNWRF.mjs.map +0 -7
  220. package/dist/lib/node-esm/hooks/index.mjs +0 -18
  221. package/dist/lib/node-esm/index.mjs +0 -474
  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/open-OI24QNTO.mjs +0 -28
  225. package/dist/lib/node-esm/open-OI24QNTO.mjs.map +0 -7
  226. package/dist/lib/node-esm/operations/index.mjs +0 -14
  227. package/dist/lib/node-esm/operations/index.mjs.map +0 -7
  228. package/dist/lib/node-esm/summarize-3UNN24I2.mjs +0 -79
  229. package/dist/lib/node-esm/summarize-3UNN24I2.mjs.map +0 -7
  230. package/dist/lib/node-esm/types/index.mjs +0 -11
  231. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +0 -6
  232. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +0 -1
  233. package/dist/types/src/containers/TranscriptionContainer/index.d.ts +0 -2
  234. package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +0 -1
  235. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +0 -8
  236. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +0 -1
  237. package/dist/types/src/operations/definitions.d.ts.map +0 -1
  238. package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
  239. package/dist/types/src/stories/useIsSpeaking.d.ts +0 -3
  240. package/dist/types/src/stories/useIsSpeaking.d.ts.map +0 -1
  241. package/dist/types/src/types/capabilities.d.ts +0 -19
  242. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  243. package/dist/types/src/types/defs.d.ts +0 -5
  244. package/dist/types/src/types/defs.d.ts.map +0 -1
  245. package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +0 -35
  246. package/src/containers/TranscriptionContainer/index.ts +0 -5
  247. package/src/hooks/useQueueModelAdapter.ts +0 -66
  248. package/src/stories/MicrophoneTranscription.stories.tsx +0 -264
  249. package/src/stories/useIsSpeaking.tsx +0 -40
  250. package/src/types/capabilities.ts +0 -39
  251. package/src/types/defs.ts +0 -9
  252. /package/dist/lib/{browser/hooks → neutral/blueprints}/index.mjs.map +0 -0
  253. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
  254. /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
  255. /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs +0 -0
  256. /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs.map +0 -0
  257. /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs +0 -0
  258. /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs.map +0 -0
  259. /package/dist/lib/{browser/types → neutral/hooks}/index.mjs.map +0 -0
  260. /package/dist/lib/{node-esm/hooks → neutral}/index.mjs.map +0 -0
  261. /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
  262. /package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
@@ -3,57 +3,50 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import * as Effect from 'effect/Effect';
7
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
8
7
 
9
- import { withPluginManager } from '@dxos/app-framework/testing';
10
- import { AppActivationEvents } from '@dxos/app-toolkit';
11
- import { scheduleTask } from '@dxos/async';
12
- import { Context } from '@dxos/context';
13
- import { Obj } from '@dxos/echo';
14
- import { MemoryQueue } from '@dxos/echo-db';
15
- import { createQueueDXN } from '@dxos/echo/internal';
16
- import { FunctionExecutor, ServiceContainer } from '@dxos/functions-runtime';
17
- import { log } from '@dxos/log';
18
- import { ClientPlugin } from '@dxos/plugin-client';
19
- import { initializeIdentity } from '@dxos/plugin-client/testing';
20
- import { PreviewPlugin } from '@dxos/plugin-preview';
21
- import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
22
- import { withLayout } from '@dxos/react-ui/testing';
23
- import { TestSchema } from '@dxos/schema/testing';
24
- import { type Actor, Message, Organization, Person } from '@dxos/types';
25
- import { seedTestData } from '@dxos/types/testing';
8
+ import { useAudioFile } from '#hooks';
26
9
 
27
- import { useAudioFile, useQueueModelAdapter, useTranscriber } from '#hooks';
28
- import { TestItem } from '#testing';
29
-
30
- import { MessageNormalizer, getActorId } from '../normalization';
31
10
  import { type MediaStreamRecorderProps, type TranscriberProps } from '../transcriber';
32
- import { TranscriptionPlugin } from '../TranscriptionPlugin';
33
- import { renderByline } from '../util';
11
+ import {
12
+ useIsSpeaking,
13
+ createStoryDecorators,
14
+ useStoryAppendSegments,
15
+ useStoryMessageModel,
16
+ useStoryTranscriber,
17
+ } from './common';
34
18
  import { TranscriptionStory } from './TranscriptionStory';
35
- import { useIsSpeaking } from './useIsSpeaking';
36
19
 
37
- const AudioFile = ({
38
- detectSpeaking,
39
- audioUrl,
40
- normalizeSentences,
41
- transcriberConfig,
42
- recorderConfig,
43
- audioConstraints,
44
- }: {
20
+ const DEFAULT_TRANSCRIBER_CONFIG = {
21
+ transcribeAfterChunksAmount: 100,
22
+ prefixBufferChunksAmount: 50,
23
+ normalizeSentences: true,
24
+ };
25
+
26
+ const DEFAULT_RECORDER_CONFIG = {
27
+ interval: 200,
28
+ };
29
+
30
+ type DefaultStoryProps = {
45
31
  detectSpeaking?: boolean;
46
32
  normalizeSentences?: boolean;
47
33
  audioUrl: string;
48
34
  transcriberConfig?: TranscriberProps['config'];
49
35
  recorderConfig?: MediaStreamRecorderProps['config'];
50
36
  audioConstraints?: MediaTrackConstraints;
51
- }) => {
37
+ };
38
+
39
+ const DefaultStory = ({
40
+ detectSpeaking,
41
+ audioUrl,
42
+ normalizeSentences,
43
+ transcriberConfig = DEFAULT_TRANSCRIBER_CONFIG,
44
+ recorderConfig = DEFAULT_RECORDER_CONFIG,
45
+ audioConstraints,
46
+ }: DefaultStoryProps) => {
52
47
  const [running, setRunning] = useState(false);
53
- const actor: Actor.Actor = useMemo(() => ({ name: 'You' }), []);
54
48
 
55
- // Optional uploaded file overrides the default URL.
56
- // The useEffect below revokes the previous URL whenever it changes (and on unmount).
49
+ // Optional uploaded file overrides the default URL; revoke the previous URL on change/unmount.
57
50
  const [uploadedUrl, setUploadedUrl] = useState<string>();
58
51
  useEffect(() => {
59
52
  return () => {
@@ -69,12 +62,10 @@ const AudioFile = ({
69
62
  }, []);
70
63
 
71
64
  const sourceUrl = uploadedUrl ?? audioUrl;
72
-
73
- // Audio.
74
65
  const { audio, track, stream } = useAudioFile(sourceUrl, audioConstraints);
75
-
76
- // Speaking.
77
66
  const isSpeaking = detectSpeaking ? useIsSpeaking(track) : true;
67
+
68
+ // Pipe the decoded stream into the <audio> element so the user hears playback.
78
69
  const ref = useRef<HTMLAudioElement>(null);
79
70
  useEffect(() => {
80
71
  if (ref.current && stream) {
@@ -82,6 +73,7 @@ const AudioFile = ({
82
73
  }
83
74
  }, [stream]);
84
75
 
76
+ // Mirror the toolbar's running flag to the underlying HTMLAudioElement.
85
77
  useEffect(() => {
86
78
  if (!audio) {
87
79
  return;
@@ -94,176 +86,58 @@ const AudioFile = ({
94
86
  }
95
87
  }, [audio, running]);
96
88
 
97
- // Transcriber.
98
- // TODO(dmaretskyi): Use space.queues.create() instead.
99
- const queueDxn = useMemo(() => createQueueDXN(), []);
100
- const queue = useMemo(() => new MemoryQueue<Message.Message>(queueDxn), [queueDxn]);
101
-
102
- const model = useQueueModelAdapter(renderByline([]), queue);
103
- const handleSegments = useCallback<TranscriberProps['onSegments']>(
104
- async (blocks) => {
105
- void queue?.append([
106
- Obj.make(Message.Message, {
107
- created: new Date().toISOString(),
108
- sender: actor,
109
- blocks,
110
- }),
111
- ]);
112
- },
113
- [queue],
114
- );
89
+ const { model, appendMessage } = useStoryMessageModel();
90
+ const handleSegments = useStoryAppendSegments(appendMessage);
115
91
 
116
- const transcriber = useTranscriber({
92
+ useStoryTranscriber({
117
93
  audioStreamTrack: track,
118
- onSegments: handleSegments,
94
+ running,
95
+ isSpeaking,
119
96
  transcriberConfig,
120
97
  recorderConfig,
98
+ onSegments: handleSegments,
121
99
  });
122
100
 
123
- // Normalize sentences.
124
- const normalizer = useMemo(() => {
125
- if (!normalizeSentences) {
126
- return;
127
- }
128
- const executor = new FunctionExecutor(
129
- new ServiceContainer().setServices({
130
- // ai: {
131
- // client: new Edge AiServiceClient({
132
- // endpoint: AI_SERVICE_ENDPOINT.REMOTE,
133
- // defaultGenerationOptions: {
134
- // model: '@anthropic/claude-3-5-sonnet-20241022',
135
- // },
136
- // }),
137
- // },
138
- }),
139
- );
140
-
141
- return new MessageNormalizer({
142
- functionExecutor: executor,
143
- queue,
144
- startingCursor: { actorId: getActorId(actor), timestamp: new Date().toISOString() },
145
- });
146
- }, [normalizeSentences, queue, actor]);
147
-
148
- useEffect(() => {
149
- if (!normalizer) {
150
- return;
151
- }
152
- const ctx = new Context();
153
- scheduleTask(ctx, async () => {
154
- await normalizer.open();
155
- ctx.onDispose(async () => {
156
- await normalizer.close();
157
- });
158
- });
159
- return () => {
160
- void ctx.dispose();
161
- };
162
- }, [normalizer]);
163
-
164
- const manageChunkRecording = () => {
165
- if (running && isSpeaking && transcriber) {
166
- log.info('starting transcription');
167
- transcriber?.startChunksRecording();
168
- } else if ((!isSpeaking || !running) && transcriber) {
169
- log.info('stopping transcription');
170
- transcriber?.stopChunksRecording();
171
- }
172
- };
173
-
174
- useEffect(() => {
175
- const ctx = new Context();
176
- scheduleTask(ctx, async () => {
177
- if (transcriber && running) {
178
- await transcriber.open();
179
- } else if (!running) {
180
- await transcriber?.close();
181
- }
182
- manageChunkRecording();
183
- });
184
-
185
- return () => {
186
- void ctx.dispose();
187
- };
188
- }, [transcriber, running]);
189
-
190
- useEffect(() => {
191
- manageChunkRecording();
192
- }, [isSpeaking, stream]);
101
+ // TODO(burdon): Sentence normalization moved to require a real space-backed Feed + FeedService runtime.
102
+ // Re-enable here once the story has access to one.
103
+ void normalizeSentences;
193
104
 
194
105
  return (
195
106
  <TranscriptionStory
107
+ audioRef={ref}
196
108
  disabled={!stream}
197
109
  model={model}
198
110
  running={running}
199
111
  onRunningChange={setRunning}
200
- audioRef={ref}
201
112
  onUpload={handleUpload}
202
113
  />
203
114
  );
204
115
  };
205
116
 
206
117
  const meta = {
207
- title: 'plugins/plugin-transcription/components/FileTranscription',
208
- decorators: [
209
- withLayout({ layout: 'column' }),
210
- withPluginManager({
211
- plugins: [
212
- ...corePlugins(),
213
- StorybookPlugin({}),
214
- ClientPlugin({
215
- types: [TestItem, Person.Person, Organization.Organization, TestSchema.DocumentType],
216
- onClientInitialized: ({ client }) =>
217
- Effect.gen(function* () {
218
- const { personalSpace } = yield* initializeIdentity(client);
219
- yield* Effect.promise(() => seedTestData(personalSpace));
220
- }),
221
- }),
222
-
223
- PreviewPlugin(),
224
- TranscriptionPlugin(),
225
- ],
226
- fireEvents: [AppActivationEvents.SetupAppGraph],
227
- }),
228
- ],
229
- } satisfies Meta;
118
+ title: 'plugins/plugin-transcription/stories/FileTranscription',
119
+ render: DefaultStory,
120
+ decorators: createStoryDecorators(),
121
+ } satisfies Meta<DefaultStoryProps>;
230
122
 
231
123
  export default meta;
232
124
 
233
125
  type Story = StoryObj<typeof meta>;
234
126
 
235
- const TRANSCRIBER_CONFIG = {
236
- transcribeAfterChunksAmount: 100,
237
- prefixBufferChunksAmount: 50,
238
- normalizeSentences: true,
239
- };
240
-
241
- const RECORDER_CONFIG = {
242
- interval: 200,
243
- };
244
-
245
- export const Default: StoryObj<typeof AudioFile> = {
246
- render: AudioFile,
127
+ export const Default: Story = {
247
128
  args: {
248
129
  detectSpeaking: true,
249
130
  // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
250
131
  audioUrl: 'https://dxos.network/audio-london.m4a',
251
132
  // textUrl: 'https://dxos.network/audio-london.txt',
252
- transcriberConfig: TRANSCRIBER_CONFIG,
253
- recorderConfig: RECORDER_CONFIG,
254
133
  },
255
134
  };
256
135
 
257
136
  // TODO(mykola): Fix sentence normalization.
258
137
  // export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
259
- // render: AudioFile,
260
138
  // args: {
261
139
  // detectSpeaking: true,
262
140
  // normalizeSentences: true,
263
- // // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
264
141
  // audioUrl: 'https://dxos.network/audio-london.m4a',
265
- // // textUrl: 'https://dxos.network/audio-london.txt',
266
- // transcriberConfig: TRANSCRIBER_CONFIG,
267
- // recorderConfig: RECORDER_CONFIG,
268
142
  // },
269
143
  // };
@@ -23,7 +23,6 @@ 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
 
26
- const [transcript, setTranscript] = useState('');
27
26
  const recognition = useMemo(() => {
28
27
  // TODO(mykola): Fix types
29
28
  const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
@@ -38,16 +37,16 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
38
37
  recognition.interimResults = true;
39
38
  recognition.lang = 'en-US';
40
39
  recognition.maxAlternatives = 1;
41
- // Add this line to enable punctuation
40
+ // Add this line to enable punctuation.
42
41
  recognition.grammars = new SpeechGrammarList();
43
42
 
44
- // TODO(mykola): Fix types
43
+ // TODO(mykola): Fix types.
45
44
  recognition.onresult = (event: any) => {
46
45
  log.info('recognition result', { event });
47
46
  const current = event.resultIndex;
48
47
  const transcript = event.results[current][0].transcript;
49
48
 
50
- // Remove punctuation and normalize whitespace for comparison
49
+ // Remove punctuation and normalize whitespace for comparison.
51
50
  const normalizeText = (text: string) =>
52
51
  text
53
52
  .toLowerCase()
@@ -57,7 +56,7 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
57
56
 
58
57
  const normalizedTranscript = normalizeText(transcript);
59
58
 
60
- // Check each keyword and update matching words
59
+ // Check each keyword and update matching words.
61
60
  const updatedWords = matchingWords.map((word) => {
62
61
  const normalizedKeyword = normalizeText(word.text);
63
62
  const matched = normalizedTranscript.includes(normalizedKeyword);
@@ -65,10 +64,9 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
65
64
  });
66
65
 
67
66
  setMatchingWords(updatedWords);
68
- setTranscript(transcript);
69
67
  };
70
68
 
71
- // TODO(mykola): Fix types
69
+ // TODO(mykola): Fix types.
72
70
  recognition.onerror = (event: any) => {
73
71
  log.error('Speech recognition error:', { error: event.error });
74
72
  setRunning(false);
@@ -99,7 +97,7 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
99
97
  </Panel.Toolbar>
100
98
 
101
99
  <Panel.Content>
102
- <div className='p-4 grid grid-cols-[repeat(auto-fill,minmax(6rem,1fr))] gap-2'>
100
+ <div className='p-2 grid grid-cols-[repeat(auto-fill,minmax(6rem,1fr))] gap-2'>
103
101
  {matchingWords.map((word, index) => (
104
102
  <span
105
103
  key={index}
@@ -118,7 +116,7 @@ const DefaultStory = ({ keywords }: DefaultStoryProps) => {
118
116
  };
119
117
 
120
118
  const meta = {
121
- title: 'plugins/plugin-transcription/components/KeyWordDetection',
119
+ title: 'plugins/plugin-transcription/stories/KeyWordDetection',
122
120
  render: DefaultStory,
123
121
  decorators: [withTheme(), withLayout({ layout: 'column' })],
124
122
  } satisfies Meta<typeof DefaultStory>;
@@ -0,0 +1,153 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React, { useCallback, useMemo, useState } from 'react';
7
+
8
+ import {
9
+ type ExtractionFunction,
10
+ extractionAnthropicFunction,
11
+ extractionNerFunction,
12
+ getNer,
13
+ processTranscriptMessage,
14
+ } from '@dxos/assistant/extraction';
15
+ import { Filter, type Obj } from '@dxos/echo';
16
+ import { invariant } from '@dxos/invariant';
17
+ import { useSpaces } from '@dxos/react-client/echo';
18
+ import { Message, Organization, Person } from '@dxos/types';
19
+
20
+ import { useAudioTrack } from '#hooks';
21
+
22
+ import { type MediaStreamRecorderProps, type TranscriberProps } from '../transcriber';
23
+ import { useIsSpeaking, createStoryDecorators, useStoryMessageModel, useStoryTranscriber } from './common';
24
+ import { TranscriptionStory } from './TranscriptionStory';
25
+
26
+ const DEFAULT_TRANSCRIBER_CONFIG = {
27
+ transcribeAfterChunksAmount: 50,
28
+ prefixBufferChunksAmount: 10,
29
+ };
30
+
31
+ const DEFAULT_RECORDER_CONFIG = {
32
+ interval: 200,
33
+ };
34
+
35
+ type DefaultStoryProps = {
36
+ detectSpeaking?: boolean;
37
+ transcriberConfig?: TranscriberProps['config'];
38
+ recorderConfig?: MediaStreamRecorderProps['config'];
39
+ audioConstraints?: MediaTrackConstraints;
40
+ entityExtraction?: 'ner' | 'llm';
41
+ };
42
+
43
+ const DefaultStory = ({
44
+ detectSpeaking,
45
+ entityExtraction,
46
+ transcriberConfig = DEFAULT_TRANSCRIBER_CONFIG,
47
+ recorderConfig = DEFAULT_RECORDER_CONFIG,
48
+ audioConstraints,
49
+ }: DefaultStoryProps) => {
50
+ const [running, setRunning] = useState(false);
51
+ const track = useAudioTrack(running, audioConstraints);
52
+ const isSpeaking = detectSpeaking ? useIsSpeaking(track) : true;
53
+ const { model, appendMessage } = useStoryMessageModel();
54
+ const [space] = useSpaces();
55
+
56
+ // Resolve the entity-extraction function + nearby objects once per `entityExtraction` toggle.
57
+ const { extractionFunction, objects } = useMemo(() => {
58
+ if (!space || !entityExtraction) {
59
+ return {};
60
+ }
61
+
62
+ let extractionFunction: ExtractionFunction | undefined;
63
+ let objects: Promise<Obj.Unknown[]> | undefined;
64
+ if (entityExtraction === 'ner') {
65
+ void getNer(); // Init model loading. Takes time.
66
+ extractionFunction = extractionNerFunction;
67
+ } else if (entityExtraction === 'llm') {
68
+ extractionFunction = extractionAnthropicFunction;
69
+ objects = space.db.query(Filter.or(Filter.type(Person.Person), Filter.type(Organization.Organization))).run();
70
+ }
71
+ return { extractionFunction, objects };
72
+ }, [entityExtraction, space]);
73
+
74
+ // Build a Message from each transcribed batch; optionally enrich via entity extraction.
75
+ const handleSegments = useCallback<TranscriberProps['onSegments']>(
76
+ async (blocks) => {
77
+ const message = Message.make({
78
+ sender: { name: 'You' },
79
+ created: new Date().toISOString(),
80
+ blocks,
81
+ });
82
+
83
+ if (entityExtraction && space) {
84
+ invariant(extractionFunction, 'extractionFunction is required');
85
+ const result = await processTranscriptMessage({
86
+ function: extractionFunction,
87
+ input: { message, objects: await objects },
88
+ options: { fallbackToRaw: true, timeout: 30_000 },
89
+ });
90
+ appendMessage(result.message);
91
+ } else {
92
+ appendMessage(message);
93
+ }
94
+ },
95
+ [appendMessage, space, entityExtraction, extractionFunction, objects],
96
+ );
97
+
98
+ useStoryTranscriber({
99
+ audioStreamTrack: track,
100
+ running,
101
+ isSpeaking,
102
+ transcriberConfig,
103
+ recorderConfig,
104
+ onSegments: handleSegments,
105
+ });
106
+
107
+ return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} />;
108
+ };
109
+
110
+ const meta = {
111
+ title: 'plugins/plugin-transcription/stories/LiveTranscription',
112
+ render: DefaultStory,
113
+ decorators: createStoryDecorators({ enableVectorIndex: true }),
114
+ } satisfies Meta<typeof DefaultStory>;
115
+
116
+ export default meta;
117
+
118
+ type Story = StoryObj<typeof meta>;
119
+
120
+ export const Default: Story = {
121
+ args: {
122
+ detectSpeaking: false,
123
+ audioConstraints: {
124
+ echoCancellation: false,
125
+ noiseSuppression: false,
126
+ autoGainControl: false,
127
+ },
128
+ },
129
+ };
130
+
131
+ export const SpeechDetection: Story = {
132
+ args: {
133
+ detectSpeaking: true,
134
+ audioConstraints: {
135
+ echoCancellation: true,
136
+ noiseSuppression: true,
137
+ autoGainControl: true,
138
+ },
139
+ },
140
+ };
141
+
142
+ // TODO(mykola): Fix hugging face quota issues.
143
+ // export const EntityExtraction: Story = {
144
+ // args: {
145
+ // detectSpeaking: true,
146
+ // entityExtraction: 'ner',
147
+ // audioConstraints: {
148
+ // echoCancellation: true,
149
+ // noiseSuppression: true,
150
+ // autoGainControl: true,
151
+ // },
152
+ // },
153
+ // };
@@ -2,15 +2,12 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import React, {
8
6
  type ChangeEvent,
9
7
  type Dispatch,
10
- type FC,
11
- type ReactNode,
12
8
  type RefObject,
13
9
  type SetStateAction,
10
+ useCallback,
14
11
  useRef,
15
12
  } from 'react';
16
13
 
@@ -20,34 +17,46 @@ import { type Message } from '@dxos/types';
20
17
  import { Transcription } from '../components';
21
18
  import { type SerializationModel } from '../model';
22
19
 
20
+ export type TranscriptionStoryProps = {
21
+ audioRef?: RefObject<HTMLAudioElement | null>;
22
+ model: SerializationModel<Message.Message>;
23
+ running: boolean;
24
+ disabled?: boolean;
25
+ uploadAccept?: string;
26
+ onRunningChange: Dispatch<SetStateAction<boolean>>;
27
+ onUpload?: (file: File) => void;
28
+ };
29
+
23
30
  /**
24
31
  * Story wrapper that renders a transcript with playback controls, an optional audio file
25
32
  * upload affordance, and a toolbar slot for additional controls.
26
33
  *
27
34
  * @param onUpload - Callback fired when the user picks a local audio file via the upload button.
28
35
  * @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
36
  */
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 }) => {
37
+ export const TranscriptionStory = ({
38
+ audioRef,
39
+ model,
40
+ running,
41
+ disabled,
42
+ uploadAccept = 'audio/*',
43
+ onRunningChange,
44
+ onUpload,
45
+ }: TranscriptionStoryProps) => {
41
46
  const fileInputRef = useRef<HTMLInputElement>(null);
42
47
 
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
- };
48
+ const handleFileChange = useCallback(
49
+ (event: ChangeEvent<HTMLInputElement>) => {
50
+ const file = event.target.files?.[0];
51
+ if (file && onUpload) {
52
+ onUpload(file);
53
+ }
54
+
55
+ // Reset so the same file can be re-selected.
56
+ event.target.value = '';
57
+ },
58
+ [onUpload],
59
+ );
51
60
 
52
61
  return (
53
62
  <>
@@ -79,7 +88,6 @@ export const TranscriptionStory: FC<{
79
88
  />
80
89
  </>
81
90
  )}
82
- {toolbarSlot}
83
91
  </Toolbar.Root>
84
92
  <ScrollContainer.Root pin>
85
93
  <ScrollContainer.Content>