@dxos/plugin-transcription 0.7.5-main.b19bfc8

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