@dxos/plugin-transcription 0.8.3 → 0.8.4-main.1da679c

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 (198) hide show
  1. package/dist/lib/browser/TranscriptContainer-W6CHJPIB.mjs +11 -0
  2. package/dist/lib/browser/blueprint-definition-C62MH2FL.mjs +142 -0
  3. package/dist/lib/browser/blueprint-definition-C62MH2FL.mjs.map +7 -0
  4. package/dist/lib/browser/{chunk-GOF7MJNV.mjs → chunk-3X7HP44M.mjs} +9 -2
  5. package/dist/lib/browser/{chunk-GOF7MJNV.mjs.map → chunk-3X7HP44M.mjs.map} +1 -1
  6. package/dist/lib/browser/{chunk-IQ5ZRMHZ.mjs → chunk-BA64VLLR.mjs} +93 -102
  7. package/dist/lib/browser/chunk-BA64VLLR.mjs.map +7 -0
  8. package/dist/lib/browser/{chunk-53AXUEIQ.mjs → chunk-DHMLTHPB.mjs} +424 -442
  9. package/dist/lib/browser/chunk-DHMLTHPB.mjs.map +7 -0
  10. package/dist/lib/browser/chunk-HXVGV7T6.mjs +63 -0
  11. package/dist/lib/browser/chunk-HXVGV7T6.mjs.map +7 -0
  12. package/dist/lib/browser/{chunk-W2KNYJD6.mjs → chunk-TNEQFHIU.mjs} +3 -3
  13. package/dist/lib/browser/{chunk-W2KNYJD6.mjs.map → chunk-TNEQFHIU.mjs.map} +1 -1
  14. package/dist/lib/browser/{chunk-UDCCT32F.mjs → chunk-ZNCGBPXR.mjs} +3 -3
  15. package/dist/lib/browser/index.mjs +27 -21
  16. package/dist/lib/browser/index.mjs.map +3 -3
  17. package/dist/lib/browser/intent-resolver-LKRGWUX2.mjs +25 -0
  18. package/dist/lib/browser/intent-resolver-LKRGWUX2.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-surface-HMJS5FSS.mjs → react-surface-2AGVHCYU.mjs} +11 -11
  21. package/dist/lib/browser/react-surface-2AGVHCYU.mjs.map +7 -0
  22. package/dist/lib/browser/{transcriber-OYPLZFG5.mjs → transcriber-L3NQP2OM.mjs} +8 -8
  23. package/dist/lib/browser/transcriber-L3NQP2OM.mjs.map +7 -0
  24. package/dist/lib/browser/types/index.mjs +6 -8
  25. package/dist/lib/node-esm/{TranscriptContainer-G75LDEO5.mjs → TranscriptContainer-LJLH47VF.mjs} +4 -4
  26. package/dist/lib/node-esm/blueprint-definition-PH3DDDHR.mjs +143 -0
  27. package/dist/lib/node-esm/blueprint-definition-PH3DDDHR.mjs.map +7 -0
  28. package/dist/lib/node-esm/{chunk-MFJPEIVB.mjs → chunk-3QAIZGMW.mjs} +3 -3
  29. package/dist/lib/node-esm/{chunk-MFJPEIVB.mjs.map → chunk-3QAIZGMW.mjs.map} +1 -1
  30. package/dist/lib/node-esm/{chunk-PRL64SRU.mjs → chunk-J5GQ3KGX.mjs} +93 -102
  31. package/dist/lib/node-esm/chunk-J5GQ3KGX.mjs.map +7 -0
  32. package/dist/lib/node-esm/{chunk-56QHOX4A.mjs → chunk-JQARMZNW.mjs} +424 -442
  33. package/dist/lib/node-esm/chunk-JQARMZNW.mjs.map +7 -0
  34. package/dist/lib/node-esm/{chunk-MFJO7EKD.mjs → chunk-JQBXD6KX.mjs} +3 -3
  35. package/dist/lib/node-esm/{chunk-TXMDLLBR.mjs → chunk-QARUDLHN.mjs} +8 -2
  36. package/dist/lib/node-esm/{chunk-TXMDLLBR.mjs.map → chunk-QARUDLHN.mjs.map} +1 -1
  37. package/dist/lib/node-esm/chunk-W72HVQMD.mjs +64 -0
  38. package/dist/lib/node-esm/chunk-W72HVQMD.mjs.map +7 -0
  39. package/dist/lib/node-esm/index.mjs +27 -21
  40. package/dist/lib/node-esm/index.mjs.map +3 -3
  41. package/dist/lib/node-esm/intent-resolver-AGLWBOCO.mjs +26 -0
  42. package/dist/lib/node-esm/intent-resolver-AGLWBOCO.mjs.map +7 -0
  43. package/dist/lib/node-esm/meta.json +1 -1
  44. package/dist/lib/node-esm/{react-surface-JGQSYGRR.mjs → react-surface-HPQQ3WIF.mjs} +11 -11
  45. package/dist/lib/node-esm/react-surface-HPQQ3WIF.mjs.map +7 -0
  46. package/dist/lib/node-esm/{transcriber-TVG2MBBN.mjs → transcriber-KJX4U74D.mjs} +8 -8
  47. package/dist/lib/node-esm/transcriber-KJX4U74D.mjs.map +7 -0
  48. package/dist/lib/node-esm/types/index.mjs +6 -8
  49. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  50. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -0
  51. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/capabilities.d.ts +3 -3
  53. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  54. package/dist/types/src/capabilities/index.d.ts +3 -2
  55. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  56. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  57. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  58. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  59. package/dist/types/src/capabilities/transcriber.d.ts +1 -1
  60. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  61. package/dist/types/src/components/Transcript/Transcript.d.ts +5 -5
  62. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
  63. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +30 -15
  64. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
  65. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +2 -2
  66. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -1
  67. package/dist/types/src/components/Transcript/transcript-extension.d.ts +1 -5
  68. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -1
  69. package/dist/types/src/components/TranscriptContainer.d.ts +6 -5
  70. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
  71. package/dist/types/src/components/index.d.ts +1 -4
  72. package/dist/types/src/components/index.d.ts.map +1 -1
  73. package/dist/types/src/components/stories/FileTranscription.stories.d.ts +5 -3
  74. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +1 -1
  75. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +10 -5
  76. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  77. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +7 -5
  78. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +1 -1
  79. package/dist/types/src/components/stories/TranscriptionStory.d.ts +1 -0
  80. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +1 -1
  81. package/dist/types/src/functions/index.d.ts +3 -0
  82. package/dist/types/src/functions/index.d.ts.map +1 -0
  83. package/dist/types/src/functions/open.d.ts +7 -0
  84. package/dist/types/src/functions/open.d.ts.map +1 -0
  85. package/dist/types/src/functions/summarize.d.ts +11 -0
  86. package/dist/types/src/functions/summarize.d.ts.map +1 -0
  87. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
  88. package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
  89. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  90. package/dist/types/src/segments-normalization/normalization.d.ts +375 -243
  91. package/dist/types/src/segments-normalization/normalization.d.ts.map +1 -1
  92. package/dist/types/src/testing/testing.d.ts +1 -1
  93. package/dist/types/src/testing/testing.d.ts.map +1 -1
  94. package/dist/types/src/transcriber/index.d.ts +1 -1
  95. package/dist/types/src/transcriber/index.d.ts.map +1 -1
  96. package/dist/types/src/transcriber/media-stream-recorder.d.ts +1 -1
  97. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  98. package/dist/types/src/transcriber/transcriber.d.ts +1 -1
  99. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  100. package/dist/types/src/transcriber/transcription-manager.d.ts +3 -7
  101. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  102. package/dist/types/src/translations.d.ts +11 -11
  103. package/dist/types/src/translations.d.ts.map +1 -1
  104. package/dist/types/src/types/Transcript.d.ts +30 -0
  105. package/dist/types/src/types/Transcript.d.ts.map +1 -0
  106. package/dist/types/src/types/TranscriptAction.d.ts +23 -0
  107. package/dist/types/src/types/TranscriptAction.d.ts.map +1 -0
  108. package/dist/types/src/types/index.d.ts +2 -1
  109. package/dist/types/src/types/index.d.ts.map +1 -1
  110. package/dist/types/src/types/types.d.ts +0 -26
  111. package/dist/types/src/types/types.d.ts.map +1 -1
  112. package/dist/types/tsconfig.tsbuildinfo +1 -1
  113. package/package.json +61 -59
  114. package/src/TranscriptionPlugin.tsx +13 -8
  115. package/src/capabilities/blueprint-definition.ts +31 -0
  116. package/src/capabilities/capabilities.ts +5 -5
  117. package/src/capabilities/index.ts +1 -0
  118. package/src/capabilities/intent-resolver.ts +5 -11
  119. package/src/capabilities/react-surface.tsx +2 -2
  120. package/src/capabilities/transcriber.ts +3 -2
  121. package/src/components/Transcript/Transcript.stories.tsx +26 -24
  122. package/src/components/Transcript/Transcript.tsx +20 -27
  123. package/src/components/Transcript/deprecated/OldTranscript.tsx +4 -4
  124. package/src/components/Transcript/transcript-extension.ts +4 -41
  125. package/src/components/TranscriptContainer.tsx +13 -7
  126. package/src/components/stories/FileTranscription.stories.tsx +53 -39
  127. package/src/components/stories/KeyWordRecognition.stories.tsx +12 -6
  128. package/src/components/stories/MicrophoneTranscription.stories.tsx +41 -36
  129. package/src/components/stories/TranscriptionStory.tsx +9 -5
  130. package/src/functions/index.ts +6 -0
  131. package/src/functions/open.ts +37 -0
  132. package/src/functions/summarize.ts +97 -0
  133. package/src/hooks/useAudioFile.ts +1 -10
  134. package/src/hooks/useTranscriber.ts +5 -5
  135. package/src/hooks/useVoiceInput.ts +3 -2
  136. package/src/model/model.test.ts +9 -9
  137. package/src/segments-normalization/message-normalizer.ts +1 -1
  138. package/src/segments-normalization/normalization.test.ts +19 -21
  139. package/src/segments-normalization/normalization.ts +54 -44
  140. package/src/testing/testing.ts +13 -13
  141. package/src/transcriber/index.ts +1 -1
  142. package/src/transcriber/media-stream-recorder.ts +1 -1
  143. package/src/transcriber/transcriber.browser.test.ts +4 -3
  144. package/src/transcriber/transcriber.ts +5 -7
  145. package/src/transcriber/transcription-manager.ts +18 -39
  146. package/src/translations.ts +6 -4
  147. package/src/types/{schema.ts → Transcript.ts} +11 -8
  148. package/src/types/TranscriptAction.ts +24 -0
  149. package/src/types/index.ts +2 -1
  150. package/src/types/types.ts +0 -23
  151. package/dist/lib/browser/TranscriptContainer-H3AZQ4CQ.mjs +0 -11
  152. package/dist/lib/browser/chunk-53AXUEIQ.mjs.map +0 -7
  153. package/dist/lib/browser/chunk-IQ5ZRMHZ.mjs.map +0 -7
  154. package/dist/lib/browser/chunk-OSISJM2F.mjs +0 -55
  155. package/dist/lib/browser/chunk-OSISJM2F.mjs.map +0 -7
  156. package/dist/lib/browser/intent-resolver-BGADCHVM.mjs +0 -30
  157. package/dist/lib/browser/intent-resolver-BGADCHVM.mjs.map +0 -7
  158. package/dist/lib/browser/react-surface-HMJS5FSS.mjs.map +0 -7
  159. package/dist/lib/browser/transcriber-OYPLZFG5.mjs.map +0 -7
  160. package/dist/lib/node/TranscriptContainer-KIG3GMAB.cjs +0 -32
  161. package/dist/lib/node/TranscriptContainer-KIG3GMAB.cjs.map +0 -7
  162. package/dist/lib/node/chunk-3PYT3BDM.cjs +0 -40
  163. package/dist/lib/node/chunk-3PYT3BDM.cjs.map +0 -7
  164. package/dist/lib/node/chunk-6LW5RA3K.cjs +0 -75
  165. package/dist/lib/node/chunk-6LW5RA3K.cjs.map +0 -7
  166. package/dist/lib/node/chunk-AFUZI63A.cjs +0 -42
  167. package/dist/lib/node/chunk-AFUZI63A.cjs.map +0 -7
  168. package/dist/lib/node/chunk-CVUN3UO7.cjs +0 -778
  169. package/dist/lib/node/chunk-CVUN3UO7.cjs.map +0 -7
  170. package/dist/lib/node/chunk-FKDJNJ57.cjs +0 -35
  171. package/dist/lib/node/chunk-FKDJNJ57.cjs.map +0 -7
  172. package/dist/lib/node/chunk-ZFAUMZXE.cjs +0 -518
  173. package/dist/lib/node/chunk-ZFAUMZXE.cjs.map +0 -7
  174. package/dist/lib/node/index.cjs +0 -133
  175. package/dist/lib/node/index.cjs.map +0 -7
  176. package/dist/lib/node/intent-resolver-LL3TTI4H.cjs +0 -45
  177. package/dist/lib/node/intent-resolver-LL3TTI4H.cjs.map +0 -7
  178. package/dist/lib/node/meta.json +0 -1
  179. package/dist/lib/node/react-surface-N25TBBG4.cjs +0 -56
  180. package/dist/lib/node/react-surface-N25TBBG4.cjs.map +0 -7
  181. package/dist/lib/node/transcriber-6J4FVFSZ.cjs +0 -68
  182. package/dist/lib/node/transcriber-6J4FVFSZ.cjs.map +0 -7
  183. package/dist/lib/node/types/index.cjs +0 -36
  184. package/dist/lib/node/types/index.cjs.map +0 -7
  185. package/dist/lib/node-esm/chunk-56QHOX4A.mjs.map +0 -7
  186. package/dist/lib/node-esm/chunk-FH75X4JC.mjs +0 -56
  187. package/dist/lib/node-esm/chunk-FH75X4JC.mjs.map +0 -7
  188. package/dist/lib/node-esm/chunk-PRL64SRU.mjs.map +0 -7
  189. package/dist/lib/node-esm/intent-resolver-5LVCYSJS.mjs +0 -31
  190. package/dist/lib/node-esm/intent-resolver-5LVCYSJS.mjs.map +0 -7
  191. package/dist/lib/node-esm/react-surface-JGQSYGRR.mjs.map +0 -7
  192. package/dist/lib/node-esm/transcriber-TVG2MBBN.mjs.map +0 -7
  193. package/dist/types/src/types/schema.d.ts +0 -41
  194. package/dist/types/src/types/schema.d.ts.map +0 -1
  195. /package/dist/lib/browser/{TranscriptContainer-H3AZQ4CQ.mjs.map → TranscriptContainer-W6CHJPIB.mjs.map} +0 -0
  196. /package/dist/lib/browser/{chunk-UDCCT32F.mjs.map → chunk-ZNCGBPXR.mjs.map} +0 -0
  197. /package/dist/lib/node-esm/{TranscriptContainer-G75LDEO5.mjs.map → TranscriptContainer-LJLH47VF.mjs.map} +0 -0
  198. /package/dist/lib/node-esm/{chunk-MFJO7EKD.mjs.map → chunk-JQBXD6KX.mjs.map} +0 -0
@@ -8,7 +8,6 @@ import { format } from 'date-fns/format';
8
8
  import { intervalToDuration } from 'date-fns/intervalToDuration';
9
9
 
10
10
  import { type CleanupFn, addEventListener, combine } from '@dxos/async';
11
- import { type RenderCallback } from '@dxos/react-ui-editor';
12
11
  import { type DataType } from '@dxos/schema';
13
12
 
14
13
  import { DocumentAdapter, type SerializationModel } from '../../model';
@@ -19,13 +18,12 @@ import { DocumentAdapter, type SerializationModel } from '../../model';
19
18
  export type TranscriptOptions = {
20
19
  model: SerializationModel<DataType.Message>;
21
20
  started?: Date;
22
- renderButton?: RenderCallback<{ onClick: () => void }>;
23
21
  };
24
22
 
25
23
  /**
26
24
  * Scrolling transcript with timestamps.
27
25
  */
28
- export const transcript = ({ model, started, renderButton }: TranscriptOptions): Extension => {
26
+ export const transcript = ({ model, started }: TranscriptOptions): Extension => {
29
27
  return [
30
28
  // Show timestamps in the gutter.
31
29
  gutter({
@@ -38,7 +36,7 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
38
36
  let line = view.state.doc.lineAt(from);
39
37
  while (line.from <= to) {
40
38
  const block = model.getChunkAtLine(line.number)?.blocks[0];
41
- const timestamp = block?.type === 'transcription' && block.started;
39
+ const timestamp = block?._tag === 'transcript' && block.started;
42
40
  if (timestamp) {
43
41
  builder.add(line.from, line.from, new TimestampMarker(line.number, new Date(timestamp), start));
44
42
  }
@@ -90,22 +88,6 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
90
88
  });
91
89
  };
92
90
 
93
- // Scroll button.
94
- if (renderButton) {
95
- this._controls = document.createElement('div');
96
- this._controls.classList.add('cm-controls', 'transition-opacity', 'duration-300', 'opacity-0');
97
- view.dom.appendChild(this._controls);
98
- renderButton(
99
- this._controls,
100
- {
101
- onClick: () => {
102
- scrollToBottom(false);
103
- },
104
- },
105
- view,
106
- );
107
- }
108
-
109
91
  // Event listeners.
110
92
  this._cleanup = combine(
111
93
  addEventListener(view.scrollDOM, 'scroll', () => {
@@ -131,7 +113,7 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
131
113
  );
132
114
  }
133
115
 
134
- update(update: ViewUpdate) {
116
+ update(_update: ViewUpdate) {
135
117
  // Initial sync.
136
118
  if (!this._initialized) {
137
119
  this._initialized = true;
@@ -149,25 +131,6 @@ export const transcript = ({ model, started, renderButton }: TranscriptOptions):
149
131
  ),
150
132
 
151
133
  EditorView.theme({
152
- '.cm-scroller': {
153
- overflowY: 'scroll',
154
- },
155
- '.cm-hide-scrollbar': {
156
- scrollbarWidth: 'none',
157
- '-ms-overflow-style': 'none',
158
- },
159
- '.cm-hide-scrollbar::-webkit-scrollbar': {
160
- display: 'none',
161
- },
162
- '.cm-controls': {
163
- position: 'absolute',
164
- bottom: '0.5rem',
165
- right: '0.5rem',
166
- zIndex: 1000,
167
- },
168
- '.cm-line': {
169
- paddingRight: '1rem',
170
- },
171
134
  '.cm-timestamp-gutter': {
172
135
  width: '6rem',
173
136
  paddingRight: '1rem',
@@ -218,7 +181,7 @@ const getStartTime = (started?: Date, message?: DataType.Message): Date | undefi
218
181
  return started;
219
182
  }
220
183
 
221
- if (message?.blocks[0]?.type === 'transcription' && message.blocks[0].started) {
184
+ if (message?.blocks[0]?._tag === 'transcript' && message.blocks[0].started) {
222
185
  return new Date(message.blocks[0].started);
223
186
  }
224
187
 
@@ -2,27 +2,33 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { type FC } from 'react';
5
+ import React from 'react';
6
6
 
7
7
  import { fullyQualifiedId, getSpace } from '@dxos/client/echo';
8
8
  import { useMembers, useQueue } from '@dxos/react-client/echo';
9
9
  import { StackItem } from '@dxos/react-ui-stack';
10
10
  import { type DataType } from '@dxos/schema';
11
11
 
12
- import { Transcript, renderMarkdown } from './Transcript';
13
12
  import { useQueueModelAdapter } from '../hooks';
14
- import { type TranscriptType } from '../types';
13
+ import { type Transcript } from '../types';
15
14
 
16
- export const TranscriptionContainer: FC<{ role: string; transcript: TranscriptType }> = ({ role, transcript }) => {
15
+ import { TranscriptView, renderByline } from './Transcript';
16
+
17
+ export type TranscriptionContainerProps = {
18
+ role: string;
19
+ transcript: Transcript.Transcript;
20
+ };
21
+
22
+ export const TranscriptionContainer = ({ transcript }: TranscriptionContainerProps) => {
17
23
  const attendableId = fullyQualifiedId(transcript);
18
24
  const space = getSpace(transcript);
19
25
  const members = useMembers(space?.key).map((member) => member.identity);
20
26
  const queue = useQueue<DataType.Message>(transcript.queue.dxn, { pollInterval: 1_000 });
21
- const model = useQueueModelAdapter(renderMarkdown(members), queue);
27
+ const model = useQueueModelAdapter(renderByline(members), queue);
22
28
 
23
29
  return (
24
- <StackItem.Content role={role} classNames='container-max-width pli-2'>
25
- <Transcript attendableId={attendableId} space={space} model={model} transcript={transcript} />
30
+ <StackItem.Content>
31
+ <TranscriptView attendableId={attendableId} space={space} model={model} transcript={transcript} />
26
32
  </StackItem.Content>
27
33
  );
28
34
  };
@@ -4,11 +4,9 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
9
9
 
10
- import { EdgeAiServiceClient } from '@dxos/ai';
11
- import { AI_SERVICE_ENDPOINT } from '@dxos/ai/testing';
12
10
  import { Events, IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
13
11
  import { withPluginManager } from '@dxos/app-framework/testing';
14
12
  import { scheduleTask } from '@dxos/async';
@@ -25,17 +23,18 @@ import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
25
23
  import { ThemePlugin } from '@dxos/plugin-theme';
26
24
  import { defaultTx } from '@dxos/react-ui-theme';
27
25
  import { DataType } from '@dxos/schema';
28
- import { seedTestData, Testing } from '@dxos/schema/testing';
26
+ import { Testing, seedTestData } from '@dxos/schema/testing';
29
27
  import { withLayout, withTheme } from '@dxos/storybook-utils';
30
28
 
31
- import { TranscriptionStory } from './TranscriptionStory';
32
- import { useIsSpeaking } from './useIsSpeaking';
33
- import { TranscriptionPlugin } from '../../TranscriptionPlugin';
34
29
  import { useAudioFile, useQueueModelAdapter, useTranscriber } from '../../hooks';
35
- import { getActorId, MessageNormalizer } from '../../segments-normalization';
30
+ import { MessageNormalizer, getActorId } from '../../segments-normalization';
36
31
  import { TestItem } from '../../testing';
37
32
  import { type MediaStreamRecorderParams, type TranscriberParams } from '../../transcriber';
38
- import { renderMarkdown } from '../Transcript';
33
+ import { TranscriptionPlugin } from '../../TranscriptionPlugin';
34
+ import { renderByline } from '../Transcript';
35
+
36
+ import { TranscriptionStory } from './TranscriptionStory';
37
+ import { useIsSpeaking } from './useIsSpeaking';
39
38
 
40
39
  const AudioFile = ({
41
40
  detectSpeaking,
@@ -69,7 +68,6 @@ const AudioFile = ({
69
68
 
70
69
  useEffect(() => {
71
70
  if (!audio) {
72
- log.warn('no audio');
73
71
  return;
74
72
  }
75
73
 
@@ -85,11 +83,16 @@ const AudioFile = ({
85
83
  const queueDxn = useMemo(() => createQueueDXN(), []);
86
84
  const queue = useMemo(() => new MemoryQueue<DataType.Message>(queueDxn), [queueDxn]);
87
85
 
88
- const model = useQueueModelAdapter(renderMarkdown([]), queue);
86
+ const model = useQueueModelAdapter(renderByline([]), queue);
89
87
  const handleSegments = useCallback<TranscriberParams['onSegments']>(
90
88
  async (blocks) => {
91
- const message = Obj.make(DataType.Message, { sender: actor, created: new Date().toISOString(), blocks });
92
- void queue?.append([message]);
89
+ void queue?.append([
90
+ Obj.make(DataType.Message, {
91
+ created: new Date().toISOString(),
92
+ sender: actor,
93
+ blocks,
94
+ }),
95
+ ]);
93
96
  },
94
97
  [queue],
95
98
  );
@@ -108,14 +111,14 @@ const AudioFile = ({
108
111
  }
109
112
  const executor = new FunctionExecutor(
110
113
  new ServiceContainer().setServices({
111
- ai: {
112
- client: new EdgeAiServiceClient({
113
- endpoint: AI_SERVICE_ENDPOINT.REMOTE,
114
- defaultGenerationOptions: {
115
- model: '@anthropic/claude-3-5-sonnet-20241022',
116
- },
117
- }),
118
- },
114
+ // ai: {
115
+ // client: new Edge AiServiceClient({
116
+ // endpoint: AI_SERVICE_ENDPOINT.REMOTE,
117
+ // defaultGenerationOptions: {
118
+ // model: '@anthropic/claude-3-5-sonnet-20241022',
119
+ // },
120
+ // }),
121
+ // },
119
122
  }),
120
123
  );
121
124
 
@@ -143,10 +146,10 @@ const AudioFile = ({
143
146
  }, [normalizer]);
144
147
 
145
148
  const manageChunkRecording = () => {
146
- if (running && isSpeaking) {
149
+ if (running && isSpeaking && transcriber) {
147
150
  log.info('starting transcription');
148
151
  transcriber?.startChunksRecording();
149
- } else if (!isSpeaking || !running) {
152
+ } else if ((!isSpeaking || !running) && transcriber) {
150
153
  log.info('stopping transcription');
151
154
  transcriber?.stopChunksRecording();
152
155
  }
@@ -172,10 +175,18 @@ const AudioFile = ({
172
175
  manageChunkRecording();
173
176
  }, [isSpeaking, stream]);
174
177
 
175
- return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} audioRef={ref} />;
178
+ return (
179
+ <TranscriptionStory
180
+ disabled={!stream}
181
+ model={model}
182
+ running={running}
183
+ onRunningChange={setRunning}
184
+ audioRef={ref}
185
+ />
186
+ );
176
187
  };
177
188
 
178
- const meta: Meta<typeof AudioFile> = {
189
+ const meta = {
179
190
  title: 'plugins/plugin-transcription/FileTranscription',
180
191
  decorators: [
181
192
  withPluginManager({
@@ -184,7 +195,7 @@ const meta: Meta<typeof AudioFile> = {
184
195
  StorybookLayoutPlugin(),
185
196
  ClientPlugin({
186
197
  types: [TestItem, DataType.Person, DataType.Organization, Testing.DocumentType],
187
- onClientInitialized: async (_, client) => {
198
+ onClientInitialized: async ({ client }) => {
188
199
  await client.halo.createIdentity();
189
200
  await client.spaces.waitUntilReady();
190
201
  await client.spaces.default.waitUntilReady();
@@ -202,10 +213,12 @@ const meta: Meta<typeof AudioFile> = {
202
213
  withTheme,
203
214
  withLayout({ fullscreen: true, classNames: 'justify-center' }),
204
215
  ],
205
- };
216
+ } satisfies Meta;
206
217
 
207
218
  export default meta;
208
219
 
220
+ type Story = StoryObj<typeof meta>;
221
+
209
222
  const TRANSCRIBER_CONFIG = {
210
223
  transcribeAfterChunksAmount: 100,
211
224
  prefixBufferChunksAmount: 50,
@@ -228,15 +241,16 @@ export const Default: StoryObj<typeof AudioFile> = {
228
241
  },
229
242
  };
230
243
 
231
- export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
232
- render: AudioFile,
233
- args: {
234
- detectSpeaking: true,
235
- normalizeSentences: true,
236
- // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
237
- audioUrl: 'https://dxos.network/audio-london.m4a',
238
- // textUrl: 'https://dxos.network/audio-london.txt',
239
- transcriberConfig: TRANSCRIBER_CONFIG,
240
- recorderConfig: RECORDER_CONFIG,
241
- },
242
- };
244
+ // TODO(mykola): Fix sentence normalization.
245
+ // export const WithSentenceNormalization: StoryObj<typeof AudioFile> = {
246
+ // render: AudioFile,
247
+ // args: {
248
+ // detectSpeaking: true,
249
+ // normalizeSentences: true,
250
+ // // https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
251
+ // audioUrl: 'https://dxos.network/audio-london.m4a',
252
+ // // textUrl: 'https://dxos.network/audio-london.txt',
253
+ // transcriberConfig: TRANSCRIBER_CONFIG,
254
+ // recorderConfig: RECORDER_CONFIG,
255
+ // },
256
+ // };
@@ -4,7 +4,7 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type StoryObj, type Meta } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React, { useEffect, useMemo, useState } from 'react';
9
9
 
10
10
  import { log } from '@dxos/log';
@@ -17,7 +17,11 @@ type Word = {
17
17
  matched: boolean;
18
18
  };
19
19
 
20
- const SpeechRecognition = ({ keywords }: { keywords: string[] }) => {
20
+ type StoryProps = {
21
+ keywords: string[];
22
+ };
23
+
24
+ const DefaultStory = ({ keywords }: StoryProps) => {
21
25
  const [running, setRunning] = useState(false);
22
26
  const [matchingWords, setMatchingWords] = useState<Word[]>(keywords.map((word) => ({ text: word, matched: false })));
23
27
 
@@ -113,15 +117,17 @@ const SpeechRecognition = ({ keywords }: { keywords: string[] }) => {
113
117
  );
114
118
  };
115
119
 
116
- const meta: Meta<typeof SpeechRecognition> = {
120
+ const meta = {
117
121
  title: 'plugins/plugin-transcription/KeyWordDetection',
122
+ render: DefaultStory,
118
123
  decorators: [withTheme, withLayout({ fullscreen: true, classNames: 'justify-center' })],
119
- };
124
+ } satisfies Meta<typeof DefaultStory>;
120
125
 
121
126
  export default meta;
122
127
 
123
- export const SpeechRecognitionAPI: StoryObj<typeof SpeechRecognition> = {
124
- render: SpeechRecognition,
128
+ type Story = StoryObj<typeof meta>;
129
+
130
+ export const SpeechRecognitionAPI: Story = {
125
131
  args: {
126
132
  keywords: ['kai', 'computer', 'hello'],
127
133
  },
@@ -4,20 +4,18 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
9
 
10
- import { EdgeAiServiceClient } from '@dxos/ai';
11
- import { AI_SERVICE_ENDPOINT } from '@dxos/ai/testing';
12
10
  import { Events, IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
13
11
  import { withPluginManager } from '@dxos/app-framework/testing';
14
12
  import {
15
- extractionAnthropicFn,
16
13
  type ExtractionFunction,
17
- extractionNerFn,
18
- processTranscriptMessage,
14
+ extractionAnthropicFunction,
15
+ extractionNerFunction,
19
16
  getNer,
20
- } from '@dxos/assistant';
17
+ processTranscriptMessage,
18
+ } from '@dxos/assistant/extraction';
21
19
  import { Filter, Obj, type Type } from '@dxos/echo';
22
20
  import { MemoryQueue } from '@dxos/echo-db';
23
21
  import { createQueueDXN } from '@dxos/echo-schema';
@@ -32,16 +30,17 @@ import { ThemePlugin } from '@dxos/plugin-theme';
32
30
  import { IndexKind, useSpace } from '@dxos/react-client/echo';
33
31
  import { defaultTx } from '@dxos/react-ui-theme';
34
32
  import { DataType } from '@dxos/schema';
35
- import { seedTestData, Testing } from '@dxos/schema/testing';
33
+ import { Testing, seedTestData } from '@dxos/schema/testing';
36
34
  import { withLayout, withTheme } from '@dxos/storybook-utils';
37
35
 
38
- import { TranscriptionStory } from './TranscriptionStory';
39
- import { useIsSpeaking } from './useIsSpeaking';
40
- import { TranscriptionPlugin } from '../../TranscriptionPlugin';
41
36
  import { useAudioTrack, useQueueModelAdapter, useTranscriber } from '../../hooks';
42
37
  import { TestItem } from '../../testing';
43
38
  import { type MediaStreamRecorderParams, type TranscriberParams } from '../../transcriber';
44
- import { renderMarkdown } from '../Transcript';
39
+ import { TranscriptionPlugin } from '../../TranscriptionPlugin';
40
+ import { renderByline } from '../Transcript';
41
+
42
+ import { TranscriptionStory } from './TranscriptionStory';
43
+ import { useIsSpeaking } from './useIsSpeaking';
45
44
 
46
45
  const TRANSCRIBER_CONFIG = {
47
46
  transcribeAfterChunksAmount: 50,
@@ -79,7 +78,7 @@ const DefaultStory = ({
79
78
  // TODO(dmaretskyi): Use space.queues.create() instead.
80
79
  const queueDxn = useMemo(() => createQueueDXN(), []);
81
80
  const queue = useMemo(() => new MemoryQueue<DataType.Message>(queueDxn), [queueDxn]);
82
- const model = useQueueModelAdapter(renderMarkdown([]), queue);
81
+ const model = useQueueModelAdapter(renderByline([]), queue);
83
82
  const space = useSpace();
84
83
 
85
84
  useEffect(() => {
@@ -102,9 +101,9 @@ const DefaultStory = ({
102
101
  if (entityExtraction === 'ner') {
103
102
  // Init model loading. Takes time.
104
103
  void getNer();
105
- extractionFunction = extractionNerFn;
104
+ extractionFunction = extractionNerFunction;
106
105
  } else if (entityExtraction === 'llm') {
107
- extractionFunction = extractionAnthropicFn;
106
+ extractionFunction = extractionAnthropicFunction;
108
107
  objects = space.db
109
108
  .query(
110
109
  Filter.or(
@@ -117,11 +116,15 @@ const DefaultStory = ({
117
116
  .then((result) => result.objects);
118
117
  }
119
118
  if (entityExtraction !== 'none') {
120
- const AiService = new EdgeAiServiceClient({
121
- endpoint: AI_SERVICE_ENDPOINT.REMOTE,
122
- });
123
119
  executor = new FunctionExecutor(
124
- new ServiceContainer().setServices({ ai: { client: AiService }, database: { db: space!.db } }),
120
+ new ServiceContainer().setServices({
121
+ // ai: {
122
+ // client: new Edge AiServiceClient({
123
+ // endpoint: AI_SERVICE_ENDPOINT.REMOTE,
124
+ // }),
125
+ // },
126
+ // database: { db: space!.db },
127
+ }),
125
128
  );
126
129
  }
127
130
 
@@ -190,7 +193,7 @@ const DefaultStory = ({
190
193
  return <TranscriptionStory model={model} running={running} onRunningChange={setRunning} />;
191
194
  };
192
195
 
193
- const meta: Meta<typeof DefaultStory> = {
196
+ const meta = {
194
197
  title: 'plugins/plugin-transcription/MicrophoneTranscription',
195
198
  render: DefaultStory,
196
199
  decorators: [
@@ -200,7 +203,7 @@ const meta: Meta<typeof DefaultStory> = {
200
203
  StorybookLayoutPlugin(),
201
204
  ClientPlugin({
202
205
  types: [TestItem, DataType.Person, DataType.Organization, Testing.DocumentType],
203
- onClientInitialized: async (_, client) => {
206
+ onClientInitialized: async ({ client }) => {
204
207
  await client.halo.createIdentity();
205
208
  await client.spaces.waitUntilReady();
206
209
  await client.spaces.default.waitUntilReady();
@@ -231,11 +234,11 @@ const meta: Meta<typeof DefaultStory> = {
231
234
  withTheme,
232
235
  withLayout({ fullscreen: true, classNames: 'justify-center' }),
233
236
  ],
234
- };
237
+ } satisfies Meta<typeof DefaultStory>;
235
238
 
236
239
  export default meta;
237
240
 
238
- type Story = StoryObj<typeof DefaultStory>;
241
+ type Story = StoryObj<typeof meta>;
239
242
 
240
243
  export const Default: Story = {
241
244
  args: {
@@ -251,19 +254,21 @@ export const Default: Story = {
251
254
  },
252
255
  };
253
256
 
254
- export const EntityExtraction: Story = {
255
- args: {
256
- detectSpeaking: true,
257
- entityExtraction: 'ner',
258
- transcriberConfig: TRANSCRIBER_CONFIG,
259
- recorderConfig: RECORDER_CONFIG,
260
- audioConstraints: {
261
- echoCancellation: true,
262
- noiseSuppression: true,
263
- autoGainControl: true,
264
- },
265
- },
266
- };
257
+ // NOTE: We are running out of free quota on hugging face entity extraction.
258
+ // TODO(mykola): Fix hugging face quota issues.
259
+ // export const EntityExtraction: Story = {
260
+ // args: {
261
+ // detectSpeaking: true,
262
+ // entityExtraction: 'ner',
263
+ // transcriberConfig: TRANSCRIBER_CONFIG,
264
+ // recorderConfig: RECORDER_CONFIG,
265
+ // audioConstraints: {
266
+ // echoCancellation: true,
267
+ // noiseSuppression: true,
268
+ // autoGainControl: true,
269
+ // },
270
+ // },
271
+ // };
267
272
 
268
273
  export const SpeechDetection: Story = {
269
274
  args: {
@@ -12,14 +12,15 @@ import { ScrollContainer } from '@dxos/react-ui-components';
12
12
  import { type DataType } from '@dxos/schema';
13
13
 
14
14
  import { type SerializationModel } from '../../model';
15
- import { Transcript } from '../Transcript';
15
+ import { TranscriptView } from '../Transcript';
16
16
 
17
17
  export const TranscriptionStory: FC<{
18
18
  model: SerializationModel<DataType.Message>;
19
+ disabled?: boolean;
19
20
  running: boolean;
20
21
  onRunningChange: Dispatch<SetStateAction<boolean>>;
21
22
  audioRef?: RefObject<HTMLAudioElement>;
22
- }> = ({ model, running, onRunningChange, audioRef }) => {
23
+ }> = ({ model, running, onRunningChange, audioRef, disabled }) => {
23
24
  const space = useSpace();
24
25
 
25
26
  return (
@@ -28,14 +29,17 @@ export const TranscriptionStory: FC<{
28
29
  <Toolbar.Root>
29
30
  <IconButton
30
31
  iconOnly
32
+ disabled={disabled}
31
33
  icon={running ? 'ph--pause--regular' : 'ph--play--regular'}
32
34
  label={running ? 'Pause' : 'Play'}
33
35
  onClick={() => onRunningChange((running) => !running)}
34
36
  />
35
37
  </Toolbar.Root>
36
- <ScrollContainer>
37
- <Transcript space={space} model={model} attendableId='story' />
38
- </ScrollContainer>
38
+ <ScrollContainer.Root pin>
39
+ <ScrollContainer.Content>
40
+ <TranscriptView space={space} model={model} />
41
+ </ScrollContainer.Content>
42
+ </ScrollContainer.Root>
39
43
  </div>
40
44
  );
41
45
  };
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export { default as open } from './open';
6
+ export { default as summarize } from './summarize';
@@ -0,0 +1,37 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Effect, Schema } from 'effect';
6
+
7
+ import { ArtifactId } from '@dxos/assistant';
8
+ import { Obj } from '@dxos/echo';
9
+ import { DatabaseService, QueueService, defineFunction } from '@dxos/functions';
10
+ import { DataType } from '@dxos/schema';
11
+
12
+ import { renderByline } from '../components';
13
+ import { Transcript } from '../types';
14
+
15
+ export default defineFunction({
16
+ name: 'dxos.org/function/transcription/open',
17
+ description: 'Opens and reads the contents of a transcription object.',
18
+ inputSchema: Schema.Struct({
19
+ id: ArtifactId.annotations({
20
+ description: 'The ID of the transcription object.',
21
+ }),
22
+ }),
23
+ outputSchema: Schema.Struct({
24
+ content: Schema.String,
25
+ }),
26
+ handler: Effect.fn(function* ({ data: { id } }) {
27
+ const transcript = yield* DatabaseService.resolve(ArtifactId.toDXN(id), Transcript.Transcript);
28
+ const { dxn } = yield* Effect.promise(() => transcript.queue.load());
29
+ const queue = yield* QueueService.getQueue(dxn);
30
+ yield* Effect.promise(() => queue?.queryObjects());
31
+ const content = queue?.objects
32
+ .filter((message) => Obj.instanceOf(DataType.Message, message))
33
+ .flatMap((message, index) => renderByline([])(message, index))
34
+ .join('\n\n');
35
+ return { content };
36
+ }),
37
+ });