@dxos/plugin-transcription 0.7.5-main.5ae2ba8

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 (172) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +15 -0
  3. package/dist/lib/browser/TranscriptContainer-SX6PNFEC.mjs +10 -0
  4. package/dist/lib/browser/TranscriptContainer-SX6PNFEC.mjs.map +7 -0
  5. package/dist/lib/browser/app-graph-builder-ZSSJIFY7.mjs +7 -0
  6. package/dist/lib/browser/app-graph-builder-ZSSJIFY7.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-AS364WZZ.mjs +8 -0
  8. package/dist/lib/browser/chunk-AS364WZZ.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-QIHUPVQD.mjs +80 -0
  10. package/dist/lib/browser/chunk-QIHUPVQD.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-R4HHS6FA.mjs +241 -0
  12. package/dist/lib/browser/chunk-R4HHS6FA.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-ZMES3U2D.mjs +78 -0
  14. package/dist/lib/browser/chunk-ZMES3U2D.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +652 -0
  16. package/dist/lib/browser/index.mjs.map +7 -0
  17. package/dist/lib/browser/intent-resolver-EAR2TFK2.mjs +32 -0
  18. package/dist/lib/browser/intent-resolver-EAR2TFK2.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -0
  20. package/dist/lib/browser/react-surface-FOEWLLTZ.mjs +26 -0
  21. package/dist/lib/browser/react-surface-FOEWLLTZ.mjs.map +7 -0
  22. package/dist/lib/browser/types/index.mjs +17 -0
  23. package/dist/lib/browser/types/index.mjs.map +7 -0
  24. package/dist/lib/node/TranscriptContainer-HU763SN3.cjs +31 -0
  25. package/dist/lib/node/TranscriptContainer-HU763SN3.cjs.map +7 -0
  26. package/dist/lib/node/app-graph-builder-YFBX3OTP.cjs +26 -0
  27. package/dist/lib/node/app-graph-builder-YFBX3OTP.cjs.map +7 -0
  28. package/dist/lib/node/chunk-JBN3P6ZF.cjs +97 -0
  29. package/dist/lib/node/chunk-JBN3P6ZF.cjs.map +7 -0
  30. package/dist/lib/node/chunk-QWXLQPP5.cjs +40 -0
  31. package/dist/lib/node/chunk-QWXLQPP5.cjs.map +7 -0
  32. package/dist/lib/node/chunk-UIHEGU6Q.cjs +103 -0
  33. package/dist/lib/node/chunk-UIHEGU6Q.cjs.map +7 -0
  34. package/dist/lib/node/chunk-YV74Z74I.cjs +267 -0
  35. package/dist/lib/node/chunk-YV74Z74I.cjs.map +7 -0
  36. package/dist/lib/node/index.cjs +661 -0
  37. package/dist/lib/node/index.cjs.map +7 -0
  38. package/dist/lib/node/intent-resolver-3KJZGGUQ.cjs +44 -0
  39. package/dist/lib/node/intent-resolver-3KJZGGUQ.cjs.map +7 -0
  40. package/dist/lib/node/meta.json +1 -0
  41. package/dist/lib/node/react-surface-AGGGDOUC.cjs +49 -0
  42. package/dist/lib/node/react-surface-AGGGDOUC.cjs.map +7 -0
  43. package/dist/lib/node/types/index.cjs +39 -0
  44. package/dist/lib/node/types/index.cjs.map +7 -0
  45. package/dist/lib/node-esm/TranscriptContainer-DVBXQIQ4.mjs +11 -0
  46. package/dist/lib/node-esm/TranscriptContainer-DVBXQIQ4.mjs.map +7 -0
  47. package/dist/lib/node-esm/app-graph-builder-77GQG6UF.mjs +9 -0
  48. package/dist/lib/node-esm/app-graph-builder-77GQG6UF.mjs.map +7 -0
  49. package/dist/lib/node-esm/chunk-36EQXBWR.mjs +242 -0
  50. package/dist/lib/node-esm/chunk-36EQXBWR.mjs.map +7 -0
  51. package/dist/lib/node-esm/chunk-KEG3ME4O.mjs +82 -0
  52. package/dist/lib/node-esm/chunk-KEG3ME4O.mjs.map +7 -0
  53. package/dist/lib/node-esm/chunk-WEZGRUG6.mjs +10 -0
  54. package/dist/lib/node-esm/chunk-WEZGRUG6.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-XYU6B73V.mjs +80 -0
  56. package/dist/lib/node-esm/chunk-XYU6B73V.mjs.map +7 -0
  57. package/dist/lib/node-esm/index.mjs +653 -0
  58. package/dist/lib/node-esm/index.mjs.map +7 -0
  59. package/dist/lib/node-esm/intent-resolver-UGLYRD4X.mjs +33 -0
  60. package/dist/lib/node-esm/intent-resolver-UGLYRD4X.mjs.map +7 -0
  61. package/dist/lib/node-esm/meta.json +1 -0
  62. package/dist/lib/node-esm/react-surface-6AYPLJ2B.mjs +27 -0
  63. package/dist/lib/node-esm/react-surface-6AYPLJ2B.mjs.map +7 -0
  64. package/dist/lib/node-esm/types/index.mjs +18 -0
  65. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  66. package/dist/types/src/TranscriptionPlugin.d.ts +2 -0
  67. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -0
  68. package/dist/types/src/capabilities/app-graph-builder.d.ts +180 -0
  69. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  70. package/dist/types/src/capabilities/capabilities.d.ts +4 -0
  71. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  72. package/dist/types/src/capabilities/index.d.ts +181 -0
  73. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  74. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  75. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  76. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  77. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  78. package/dist/types/src/components/Transcript/Transcript.d.ts +7 -0
  79. package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -0
  80. package/dist/types/src/components/Transcript/Transcript.stories.d.ts +9 -0
  81. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -0
  82. package/dist/types/src/components/Transcript/index.d.ts +2 -0
  83. package/dist/types/src/components/Transcript/index.d.ts.map +1 -0
  84. package/dist/types/src/components/TranscriptContainer.d.ts +7 -0
  85. package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -0
  86. package/dist/types/src/components/index.d.ts +6 -0
  87. package/dist/types/src/components/index.d.ts.map +1 -0
  88. package/dist/types/src/components/transcription.stories.d.ts +14 -0
  89. package/dist/types/src/components/transcription.stories.d.ts.map +1 -0
  90. package/dist/types/src/hooks/index.d.ts +6 -0
  91. package/dist/types/src/hooks/index.d.ts.map +1 -0
  92. package/dist/types/src/hooks/useAudioFile.d.ts +7 -0
  93. package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -0
  94. package/dist/types/src/hooks/useAudioTrack.d.ts +5 -0
  95. package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -0
  96. package/dist/types/src/hooks/useIsSpeaking.d.ts +2 -0
  97. package/dist/types/src/hooks/useIsSpeaking.d.ts.map +1 -0
  98. package/dist/types/src/hooks/useTranscriber.d.ts +10 -0
  99. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -0
  100. package/dist/types/src/hooks/useVoiceInput.d.ts +11 -0
  101. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -0
  102. package/dist/types/src/index.d.ts +7 -0
  103. package/dist/types/src/index.d.ts.map +1 -0
  104. package/dist/types/src/meta.d.ts +11 -0
  105. package/dist/types/src/meta.d.ts.map +1 -0
  106. package/dist/types/src/sanity.test.d.ts +2 -0
  107. package/dist/types/src/sanity.test.d.ts.map +1 -0
  108. package/dist/types/src/transcriber/audio-recorder.d.ts +47 -0
  109. package/dist/types/src/transcriber/audio-recorder.d.ts.map +1 -0
  110. package/dist/types/src/transcriber/index.d.ts +4 -0
  111. package/dist/types/src/transcriber/index.d.ts.map +1 -0
  112. package/dist/types/src/transcriber/media-stream-recorder.d.ts +26 -0
  113. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -0
  114. package/dist/types/src/transcriber/transcriber.d.ts +46 -0
  115. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -0
  116. package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
  117. package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
  118. package/dist/types/src/translations.d.ts +40 -0
  119. package/dist/types/src/translations.d.ts.map +1 -0
  120. package/dist/types/src/types/index.d.ts +3 -0
  121. package/dist/types/src/types/index.d.ts.map +1 -0
  122. package/dist/types/src/types/schema.d.ts +63 -0
  123. package/dist/types/src/types/schema.d.ts.map +1 -0
  124. package/dist/types/src/types/types.d.ts +28 -0
  125. package/dist/types/src/types/types.d.ts.map +1 -0
  126. package/dist/types/src/util/array.d.ts +2 -0
  127. package/dist/types/src/util/array.d.ts.map +1 -0
  128. package/dist/types/src/util/get-time-str.d.ts +2 -0
  129. package/dist/types/src/util/get-time-str.d.ts.map +1 -0
  130. package/dist/types/src/util/index.d.ts +5 -0
  131. package/dist/types/src/util/index.d.ts.map +1 -0
  132. package/dist/types/src/util/monitor-audio-level.d.ts +6 -0
  133. package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -0
  134. package/dist/types/src/util/random-queue-dxn.d.ts +3 -0
  135. package/dist/types/src/util/random-queue-dxn.d.ts.map +1 -0
  136. package/dist/types/tsconfig.tsbuildinfo +1 -0
  137. package/package.json +93 -0
  138. package/src/TranscriptionPlugin.tsx +57 -0
  139. package/src/capabilities/app-graph-builder.ts +7 -0
  140. package/src/capabilities/capabilities.ts +12 -0
  141. package/src/capabilities/index.ts +11 -0
  142. package/src/capabilities/intent-resolver.ts +24 -0
  143. package/src/capabilities/react-surface.tsx +21 -0
  144. package/src/components/Transcript/Transcript.stories.tsx +65 -0
  145. package/src/components/Transcript/Transcript.tsx +69 -0
  146. package/src/components/Transcript/index.ts +5 -0
  147. package/src/components/TranscriptContainer.tsx +167 -0
  148. package/src/components/index.ts +10 -0
  149. package/src/components/transcription.stories.tsx +198 -0
  150. package/src/hooks/index.ts +9 -0
  151. package/src/hooks/useAudioFile.ts +62 -0
  152. package/src/hooks/useAudioTrack.ts +35 -0
  153. package/src/hooks/useIsSpeaking.ts +77 -0
  154. package/src/hooks/useTranscriber.ts +62 -0
  155. package/src/hooks/useVoiceInput.ts +66 -0
  156. package/src/index.ts +11 -0
  157. package/src/meta.ts +17 -0
  158. package/src/sanity.test.ts +11 -0
  159. package/src/transcriber/audio-recorder.ts +56 -0
  160. package/src/transcriber/index.ts +7 -0
  161. package/src/transcriber/media-stream-recorder.ts +109 -0
  162. package/src/transcriber/transcriber.test.ts +230 -0
  163. package/src/transcriber/transcriber.ts +247 -0
  164. package/src/translations.ts +28 -0
  165. package/src/types/index.ts +6 -0
  166. package/src/types/schema.ts +60 -0
  167. package/src/types/types.ts +34 -0
  168. package/src/util/array.ts +14 -0
  169. package/src/util/get-time-str.ts +12 -0
  170. package/src/util/index.ts +8 -0
  171. package/src/util/monitor-audio-level.ts +58 -0
  172. package/src/util/random-queue-dxn.ts +9 -0
@@ -0,0 +1,653 @@
1
+ import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+ import {
3
+ mergeFloat64Arrays,
4
+ monitorAudioLevel
5
+ } from "./chunk-XYU6B73V.mjs";
6
+ import {
7
+ TranscriptContainer
8
+ } from "./chunk-WEZGRUG6.mjs";
9
+ import {
10
+ Transcript,
11
+ TranscriptionContainer
12
+ } from "./chunk-36EQXBWR.mjs";
13
+ import {
14
+ TRANSCRIPTION_PLUGIN,
15
+ TRANSCRIPTION_URL,
16
+ TranscriptType,
17
+ meta
18
+ } from "./chunk-KEG3ME4O.mjs";
19
+
20
+ // packages/plugins/experimental/plugin-transcription/src/capabilities/index.ts
21
+ import { lazy } from "@dxos/app-framework";
22
+
23
+ // packages/plugins/experimental/plugin-transcription/src/capabilities/capabilities.ts
24
+ import { defineCapability } from "@dxos/app-framework";
25
+ var TranscriptionCapabilities;
26
+ (function(TranscriptionCapabilities2) {
27
+ TranscriptionCapabilities2.Transcription = defineCapability(`${TRANSCRIPTION_PLUGIN}/capability/transcription`);
28
+ })(TranscriptionCapabilities || (TranscriptionCapabilities = {}));
29
+
30
+ // packages/plugins/experimental/plugin-transcription/src/capabilities/index.ts
31
+ var AppGraphBuilder = lazy(() => import("./app-graph-builder-77GQG6UF.mjs"));
32
+ var IntentResolver = lazy(() => import("./intent-resolver-UGLYRD4X.mjs"));
33
+ var ReactSurface = lazy(() => import("./react-surface-6AYPLJ2B.mjs"));
34
+
35
+ // packages/plugins/experimental/plugin-transcription/src/hooks/useAudioFile.ts
36
+ import { useEffect, useState } from "react";
37
+ import { log } from "@dxos/log";
38
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/plugins/experimental/plugin-transcription/src/hooks/useAudioFile.ts";
39
+ var useAudioFile = (audioUrl) => {
40
+ const [{ audio, stream, track }, setStream] = useState({});
41
+ useEffect(() => {
42
+ const t = setTimeout(async () => {
43
+ const response = await fetch(audioUrl);
44
+ const blob = await response.blob();
45
+ const audio2 = new Audio();
46
+ audio2.src = URL.createObjectURL(blob);
47
+ await new Promise((resolve, reject) => {
48
+ audio2.addEventListener("error", (err) => {
49
+ log.error("error", {
50
+ err
51
+ }, {
52
+ F: __dxlog_file,
53
+ L: 27,
54
+ S: void 0,
55
+ C: (f, a) => f(...a)
56
+ });
57
+ reject(err);
58
+ }, {
59
+ once: true
60
+ });
61
+ audio2.addEventListener("canplay", () => {
62
+ log.info("starting...", void 0, {
63
+ F: __dxlog_file,
64
+ L: 35,
65
+ S: void 0,
66
+ C: (f, a) => f(...a)
67
+ });
68
+ resolve();
69
+ }, {
70
+ once: true
71
+ });
72
+ audio2.load();
73
+ });
74
+ const ctx = new AudioContext();
75
+ const destination = ctx.createMediaStreamDestination();
76
+ destination.channelCount = 1;
77
+ const source = ctx.createMediaElementSource(audio2);
78
+ source.connect(destination);
79
+ setStream({
80
+ audio: audio2,
81
+ stream: destination.stream,
82
+ track: destination.stream.getAudioTracks()[0]
83
+ });
84
+ });
85
+ return () => clearTimeout(t);
86
+ }, [
87
+ audioUrl
88
+ ]);
89
+ return {
90
+ audio,
91
+ stream,
92
+ track
93
+ };
94
+ };
95
+
96
+ // packages/plugins/experimental/plugin-transcription/src/hooks/useAudioTrack.ts
97
+ import { useEffect as useEffect2, useState as useState2 } from "react";
98
+ var useAudioTrack = (active) => {
99
+ const [track, setTrack] = useState2();
100
+ useEffect2(() => {
101
+ if (!active) {
102
+ track?.stop();
103
+ setTrack(void 0);
104
+ return;
105
+ }
106
+ const initAudio = async () => {
107
+ const audio = new Audio();
108
+ audio.srcObject = await navigator.mediaDevices.getUserMedia({
109
+ audio: true
110
+ });
111
+ const [track2] = audio.srcObject.getAudioTracks();
112
+ if (track2) {
113
+ setTrack(track2);
114
+ }
115
+ };
116
+ if (active) {
117
+ void initAudio();
118
+ }
119
+ }, [
120
+ active
121
+ ]);
122
+ return track;
123
+ };
124
+
125
+ // packages/plugins/experimental/plugin-transcription/src/hooks/useIsSpeaking.ts
126
+ import { useEffect as useEffect3, useRef, useState as useState3 } from "react";
127
+ var useIsSpeaking = (mediaStreamTrack) => {
128
+ const [isSpeaking, setIsSpeaking] = useState3(false);
129
+ const isSpeakingRef = useRef(isSpeaking);
130
+ useEffect3(() => {
131
+ if (!mediaStreamTrack) {
132
+ setIsSpeaking(false);
133
+ return;
134
+ }
135
+ isSpeakingRef.current = isSpeaking;
136
+ const interval = window.setInterval(() => {
137
+ if (isSpeaking === isSpeakingRef.current) {
138
+ return;
139
+ }
140
+ setIsSpeaking(isSpeakingRef.current);
141
+ }, 50);
142
+ return () => {
143
+ clearInterval(interval);
144
+ };
145
+ }, [
146
+ isSpeaking,
147
+ isSpeakingRef,
148
+ mediaStreamTrack
149
+ ]);
150
+ useEffect3(() => {
151
+ if (!mediaStreamTrack) {
152
+ return;
153
+ }
154
+ let timeout = -1;
155
+ const cleanup = monitorAudioLevel({
156
+ mediaStreamTrack,
157
+ onMeasure: (vol) => {
158
+ const audioLevelAboveThreshold = vol > (isSpeakingRef.current ? 0.02 : 0.05);
159
+ if (audioLevelAboveThreshold) {
160
+ clearTimeout(timeout);
161
+ timeout = -1;
162
+ isSpeakingRef.current = true;
163
+ } else if (timeout === -1) {
164
+ timeout = window.setTimeout(() => {
165
+ isSpeakingRef.current = false;
166
+ timeout = -1;
167
+ }, 1e3);
168
+ }
169
+ }
170
+ });
171
+ return () => {
172
+ cleanup();
173
+ };
174
+ }, [
175
+ isSpeaking,
176
+ mediaStreamTrack
177
+ ]);
178
+ return isSpeaking;
179
+ };
180
+
181
+ // packages/plugins/experimental/plugin-transcription/src/hooks/useTranscriber.ts
182
+ import { useEffect as useEffect4, useMemo } from "react";
183
+
184
+ // packages/plugins/experimental/plugin-transcription/src/transcriber/media-stream-recorder.ts
185
+ import { MediaRecorder, register } from "extendable-media-recorder";
186
+ import { WaveFile } from "wavefile";
187
+ import { synchronized } from "@dxos/async";
188
+ import { invariant } from "@dxos/invariant";
189
+ import { log as log2 } from "@dxos/log";
190
+ import { trace } from "@dxos/tracing";
191
+ function _ts_decorate(decorators, target, key, desc) {
192
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
193
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
194
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
195
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
196
+ }
197
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/plugins/experimental/plugin-transcription/src/transcriber/media-stream-recorder.ts";
198
+ var initializingPromise;
199
+ var initializeExtendableMediaRecorder = async () => {
200
+ const { connect } = await import("extendable-media-recorder-wav-encoder");
201
+ await register(await connect());
202
+ };
203
+ var MediaStreamRecorder = class {
204
+ constructor({ mediaStreamTrack, interval }) {
205
+ this._mediaRecorder = void 0;
206
+ this._header = void 0;
207
+ this._mediaStreamTrack = mediaStreamTrack;
208
+ this._interval = interval;
209
+ }
210
+ get wavConfig() {
211
+ const settings = this._mediaStreamTrack.getSettings();
212
+ return {
213
+ channels: settings.channelCount ?? 1,
214
+ sampleRate: settings.sampleRate ?? 16e3,
215
+ bitDepthCode: settings.sampleSize ? String(settings.sampleSize) : "16"
216
+ };
217
+ }
218
+ setOnChunk(onChunk) {
219
+ this._onChunk = onChunk;
220
+ }
221
+ async start() {
222
+ await (initializingPromise ??= initializeExtendableMediaRecorder()).catch((err) => log2.info("initializeExtendableMediaRecorder", {
223
+ err
224
+ }, {
225
+ F: __dxlog_file2,
226
+ L: 58,
227
+ S: this,
228
+ C: (f, a) => f(...a)
229
+ }));
230
+ if (!this._mediaRecorder) {
231
+ const stream = new MediaStream([
232
+ this._mediaStreamTrack
233
+ ]);
234
+ this._mediaRecorder = new MediaRecorder(stream, {
235
+ mimeType: "audio/wav"
236
+ });
237
+ }
238
+ if (this._mediaRecorder.state === "recording") {
239
+ return;
240
+ }
241
+ invariant(this._onChunk, "MediaStreamRecorder: onChunk is not set", {
242
+ F: __dxlog_file2,
243
+ L: 69,
244
+ S: this,
245
+ A: [
246
+ "this._onChunk",
247
+ "'MediaStreamRecorder: onChunk is not set'"
248
+ ]
249
+ });
250
+ this._mediaRecorder.ondataavailable = (event) => this._ondataavailable(event);
251
+ this._mediaRecorder.start(this._interval);
252
+ }
253
+ async stop() {
254
+ if (this._mediaRecorder?.state !== "recording") {
255
+ return;
256
+ }
257
+ this._mediaRecorder.stop();
258
+ }
259
+ async _ondataavailable(event) {
260
+ const blob = event.data;
261
+ const uint8Array = new Uint8Array(await blob.arrayBuffer());
262
+ const isHeader = uint8Array[0] === 82 && // R
263
+ uint8Array[1] === 73 && // I
264
+ uint8Array[2] === 70 && // F
265
+ uint8Array[3] === 70;
266
+ let wav;
267
+ if (isHeader) {
268
+ wav = new WaveFile(uint8Array);
269
+ this._header = uint8Array.slice(0, 44);
270
+ } else if (this._header) {
271
+ wav = new WaveFile(new Uint8Array([
272
+ ...this._header,
273
+ ...uint8Array
274
+ ]));
275
+ } else {
276
+ log2.warn("MediaStreamRecorder: no header", void 0, {
277
+ F: __dxlog_file2,
278
+ L: 100,
279
+ S: this,
280
+ C: (f, a) => f(...a)
281
+ });
282
+ return;
283
+ }
284
+ this._onChunk({
285
+ timestamp: Date.now(),
286
+ data: wav.getSamples()
287
+ });
288
+ }
289
+ };
290
+ _ts_decorate([
291
+ synchronized
292
+ ], MediaStreamRecorder.prototype, "_ondataavailable", null);
293
+ MediaStreamRecorder = _ts_decorate([
294
+ trace.resource()
295
+ ], MediaStreamRecorder);
296
+
297
+ // packages/plugins/experimental/plugin-transcription/src/transcriber/transcriber.ts
298
+ import { WaveFile as WaveFile2 } from "wavefile";
299
+ import { DeferredTask, Trigger } from "@dxos/async";
300
+ import { LifecycleState, Resource } from "@dxos/context";
301
+ import { log as log3 } from "@dxos/log";
302
+ import { trace as trace2 } from "@dxos/tracing";
303
+ function _ts_decorate2(decorators, target, key, desc) {
304
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
305
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
306
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
307
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
308
+ }
309
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/plugins/experimental/plugin-transcription/src/transcriber/transcriber.ts";
310
+ var Transcriber = class extends Resource {
311
+ constructor({ config, recorder, onSegments }) {
312
+ super();
313
+ this._audioChunks = [];
314
+ this._lastUsedTimestamp = 0;
315
+ this._openTrigger = new Trigger({
316
+ autoReset: true
317
+ });
318
+ this._recording = false;
319
+ this._transcribeTask = void 0;
320
+ this._config = config;
321
+ this._recorder = recorder;
322
+ this._onSegments = onSegments;
323
+ }
324
+ async _open(ctx) {
325
+ this._recorder.setOnChunk((chunk) => this._saveAudioChunk(chunk));
326
+ await this._recorder.start();
327
+ this._transcribeTask = new DeferredTask(ctx, async () => this._transcribe());
328
+ this._openTrigger.wake();
329
+ }
330
+ async _close() {
331
+ this._recording = false;
332
+ this._transcribeTask = void 0;
333
+ await this._recorder.stop();
334
+ }
335
+ startChunksRecording() {
336
+ if (this._lifecycleState !== LifecycleState.OPEN) {
337
+ return;
338
+ }
339
+ this._recording = true;
340
+ }
341
+ stopChunksRecording() {
342
+ if (this._lifecycleState !== LifecycleState.OPEN || !this._recording) {
343
+ return;
344
+ }
345
+ this._recording = false;
346
+ this._transcribeTask?.schedule();
347
+ }
348
+ _saveAudioChunk(chunk) {
349
+ log3("saving audio chunk", {
350
+ chunk
351
+ }, {
352
+ F: __dxlog_file3,
353
+ L: 124,
354
+ S: this,
355
+ C: (f, a) => f(...a)
356
+ });
357
+ this._audioChunks.push(chunk);
358
+ if (!this._recording && (!this._transcribeTask || !this._transcribeTask.scheduled) && this._audioChunks.length >= this._config.prefixBufferChunksAmount) {
359
+ this._audioChunks = this._dropOldChunks();
360
+ } else if (this._audioChunks.length >= this._config.transcribeAfterChunksAmount && this._recording) {
361
+ this._transcribeTask?.schedule();
362
+ }
363
+ }
364
+ async _transcribe() {
365
+ log3("transcribing", {
366
+ chunks: this._audioChunks.length
367
+ }, {
368
+ F: __dxlog_file3,
369
+ L: 140,
370
+ S: this,
371
+ C: (f, a) => f(...a)
372
+ });
373
+ const chunks = this._audioChunks;
374
+ this._audioChunks = this._dropOldChunks();
375
+ if (chunks.length === 0) {
376
+ return;
377
+ }
378
+ const audio = await this._mergeAudioChunks(chunks);
379
+ const segments = await this._fetchTranscription(audio);
380
+ if (!Array.isArray(segments) || segments.length === 0) {
381
+ return;
382
+ }
383
+ const alignedSegments = this._alignSegments(segments, chunks);
384
+ if (alignedSegments.length > 0) {
385
+ await this._onSegments(alignedSegments);
386
+ }
387
+ }
388
+ _dropOldChunks() {
389
+ return this._config.prefixBufferChunksAmount > 0 ? this._audioChunks.slice(-this._config.prefixBufferChunksAmount) : [];
390
+ }
391
+ async _mergeAudioChunks(chunks) {
392
+ const file = new WaveFile2();
393
+ const wavConfig = this._recorder.wavConfig;
394
+ file.fromScratch(wavConfig.channels, wavConfig.sampleRate, wavConfig.bitDepthCode, mergeFloat64Arrays(chunks.map(({ data }) => data)));
395
+ return file.toBase64();
396
+ }
397
+ async _fetchTranscription(audio) {
398
+ if (audio.length === 0) {
399
+ this._audioChunks = [];
400
+ throw new Error("No audio to send for transcribing");
401
+ }
402
+ const response = await fetch(`${TRANSCRIPTION_URL}/transcribe`, {
403
+ method: "POST",
404
+ body: JSON.stringify({
405
+ audio
406
+ }),
407
+ headers: {
408
+ "Content-Type": "application/json"
409
+ }
410
+ });
411
+ if (!response.ok) {
412
+ this._audioChunks = [];
413
+ throw new Error("Failed to transcribe");
414
+ }
415
+ const { segments } = await response.json();
416
+ log3.info("transcription response", {
417
+ segments,
418
+ string: segments.map((segments2) => segments2.text).join(" ")
419
+ }, {
420
+ F: __dxlog_file3,
421
+ L: 206,
422
+ S: this,
423
+ C: (f, a) => f(...a)
424
+ });
425
+ return segments;
426
+ }
427
+ _alignSegments(segments, originalChunks) {
428
+ const zeroTimestamp = originalChunks.at(0).timestamp;
429
+ const filteredSegments = segments.filter((segment) => zeroTimestamp + segment.end * 1e3 >= this._lastUsedTimestamp).map((segment) => {
430
+ const words = segment.words.filter((word) => zeroTimestamp + word.start * 1e3 >= this._lastUsedTimestamp);
431
+ return {
432
+ ...segment,
433
+ words,
434
+ text: words.map((word) => word.word).join(""),
435
+ start: words.at(0)?.start ?? segment.end
436
+ };
437
+ }).filter((segment) => segment.words.length > 0);
438
+ if (filteredSegments.length === 0) {
439
+ return [];
440
+ }
441
+ this._lastUsedTimestamp = zeroTimestamp + filteredSegments.at(-1).end * 1e3;
442
+ return filteredSegments.map((segment) => ({
443
+ started: new Date(zeroTimestamp + segment.start * 1e3),
444
+ text: segment.text
445
+ }));
446
+ }
447
+ };
448
+ _ts_decorate2([
449
+ trace2.span({
450
+ showInBrowserTimeline: true
451
+ })
452
+ ], Transcriber.prototype, "_mergeAudioChunks", null);
453
+ _ts_decorate2([
454
+ trace2.span({
455
+ showInBrowserTimeline: true
456
+ })
457
+ ], Transcriber.prototype, "_fetchTranscription", null);
458
+
459
+ // packages/plugins/experimental/plugin-transcription/src/hooks/useTranscriber.ts
460
+ var RECORD_INTERVAL = 200;
461
+ var PREFIXED_CHUNKS_AMOUNT = 10;
462
+ var TRANSCRIBE_AFTER_CHUNKS_AMOUNT = 50;
463
+ var useTranscriber = ({ audioStreamTrack, onSegments }) => {
464
+ const transcriber = useMemo(() => {
465
+ if (!onSegments || !audioStreamTrack) {
466
+ return void 0;
467
+ }
468
+ return new Transcriber({
469
+ config: {
470
+ transcribeAfterChunksAmount: TRANSCRIBE_AFTER_CHUNKS_AMOUNT,
471
+ prefixBufferChunksAmount: PREFIXED_CHUNKS_AMOUNT
472
+ },
473
+ recorder: new MediaStreamRecorder({
474
+ mediaStreamTrack: audioStreamTrack,
475
+ interval: RECORD_INTERVAL
476
+ }),
477
+ onSegments
478
+ });
479
+ }, [
480
+ audioStreamTrack,
481
+ onSegments
482
+ ]);
483
+ useEffect4(() => {
484
+ return () => {
485
+ void transcriber?.close();
486
+ };
487
+ }, [
488
+ transcriber
489
+ ]);
490
+ return transcriber;
491
+ };
492
+
493
+ // packages/plugins/experimental/plugin-transcription/src/hooks/useVoiceInput.ts
494
+ import { useState as useState4, useEffect as useEffect5, useCallback } from "react";
495
+ import { scheduleMicroTask } from "@dxos/async";
496
+ import { Context } from "@dxos/context";
497
+ import { log as log4 } from "@dxos/log";
498
+ import { useSoundEffect } from "@dxos/react-ui-sfx";
499
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/plugins/experimental/plugin-transcription/src/hooks/useVoiceInput.ts";
500
+ var useVoiceInput = ({ active, onUpdate }) => {
501
+ const soundStart = useSoundEffect("StartRecording");
502
+ const soundStop = useSoundEffect("StopRecording");
503
+ const handleSegments = useCallback(async (segments) => {
504
+ const text = segments.map((str) => str.text.trim().replace(/[^\w\s]/g, "")).join(" ");
505
+ onUpdate(text);
506
+ }, []);
507
+ const [recording, setRecording] = useState4(false);
508
+ const track = useAudioTrack(active);
509
+ const transcriber = useTranscriber({
510
+ audioStreamTrack: track,
511
+ onSegments: handleSegments
512
+ });
513
+ useEffect5(() => {
514
+ const ctx = new Context(void 0, {
515
+ F: __dxlog_file4,
516
+ L: 43
517
+ });
518
+ scheduleMicroTask(ctx, async () => {
519
+ if (active && transcriber) {
520
+ await transcriber.open();
521
+ log4.info("starting...", void 0, {
522
+ F: __dxlog_file4,
523
+ L: 47,
524
+ S: void 0,
525
+ C: (f, a) => f(...a)
526
+ });
527
+ setRecording(true);
528
+ void soundStart.play();
529
+ transcriber.startChunksRecording();
530
+ } else if (!active && transcriber?.isOpen) {
531
+ transcriber?.stopChunksRecording();
532
+ await transcriber?.close();
533
+ log4.info("stopped", void 0, {
534
+ F: __dxlog_file4,
535
+ L: 54,
536
+ S: void 0,
537
+ C: (f, a) => f(...a)
538
+ });
539
+ setRecording(false);
540
+ void soundStop.play();
541
+ }
542
+ });
543
+ return () => {
544
+ void ctx.dispose();
545
+ };
546
+ }, [
547
+ active,
548
+ transcriber
549
+ ]);
550
+ return {
551
+ recording
552
+ };
553
+ };
554
+
555
+ // packages/plugins/experimental/plugin-transcription/src/TranscriptionPlugin.tsx
556
+ import { Capabilities, Events, contributes, defineModule, definePlugin } from "@dxos/app-framework";
557
+ import { ClientCapabilities, ClientEvents } from "@dxos/plugin-client";
558
+
559
+ // packages/plugins/experimental/plugin-transcription/src/translations.ts
560
+ var translations_default = [
561
+ {
562
+ "en-US": {
563
+ [TranscriptType.typename]: {
564
+ "typename label": "Transcript"
565
+ },
566
+ [TRANSCRIPTION_PLUGIN]: {
567
+ "plugin name": "Transcription",
568
+ "calls label": "Transcript",
569
+ "calls panel label": "Transcription",
570
+ "create calls label": "Create transcription",
571
+ "delete calls label": "Delete",
572
+ "delete button": "Delete",
573
+ "bookmark button": "Bookmark",
574
+ "summary button": "Create summary",
575
+ "summarizing label": "Summarizing..."
576
+ }
577
+ }
578
+ }
579
+ ];
580
+
581
+ // packages/plugins/experimental/plugin-transcription/src/TranscriptionPlugin.tsx
582
+ var TranscriptionPlugin = () => definePlugin(meta, [
583
+ defineModule({
584
+ id: `${meta.id}/module/translations`,
585
+ activatesOn: Events.SetupTranslations,
586
+ activate: () => contributes(Capabilities.Translations, translations_default)
587
+ }),
588
+ defineModule({
589
+ id: `${meta.id}/module/transcription`,
590
+ activatesOn: Events.Startup,
591
+ activate: () => contributes(TranscriptionCapabilities.Transcription, null)
592
+ }),
593
+ defineModule({
594
+ id: `${meta.id}/module/metadata`,
595
+ activatesOn: Events.SetupMetadata,
596
+ activate: () => contributes(Capabilities.Metadata, {
597
+ id: TranscriptType.typename,
598
+ metadata: {
599
+ placeholder: [
600
+ "transcript title placeholder",
601
+ {
602
+ ns: TRANSCRIPTION_PLUGIN
603
+ }
604
+ ],
605
+ icon: "ph--subtitles--regular"
606
+ }
607
+ })
608
+ }),
609
+ defineModule({
610
+ id: `${meta.id}/module/schema`,
611
+ activatesOn: ClientEvents.SetupSchema,
612
+ activate: () => [
613
+ contributes(ClientCapabilities.Schema, [
614
+ TranscriptType
615
+ ])
616
+ ]
617
+ }),
618
+ defineModule({
619
+ id: `${meta.id}/module/react-surface`,
620
+ activatesOn: Events.SetupReactSurface,
621
+ activate: ReactSurface
622
+ }),
623
+ defineModule({
624
+ id: `${meta.id}/module/intent-resolver`,
625
+ activatesOn: Events.SetupIntentResolver,
626
+ activate: IntentResolver
627
+ }),
628
+ defineModule({
629
+ id: `${meta.id}/module/app-graph-builder`,
630
+ activatesOn: Events.SetupAppGraph,
631
+ activate: AppGraphBuilder
632
+ })
633
+ ]);
634
+ export {
635
+ AppGraphBuilder,
636
+ IntentResolver,
637
+ MediaStreamRecorder,
638
+ ReactSurface,
639
+ TRANSCRIPTION_PLUGIN,
640
+ Transcriber,
641
+ Transcript,
642
+ TranscriptContainer,
643
+ TranscriptionCapabilities,
644
+ TranscriptionContainer,
645
+ TranscriptionPlugin,
646
+ meta,
647
+ useAudioFile,
648
+ useAudioTrack,
649
+ useIsSpeaking,
650
+ useTranscriber,
651
+ useVoiceInput
652
+ };
653
+ //# sourceMappingURL=index.mjs.map