@dxos/plugin-transcription 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0

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 (244) hide show
  1. package/dist/lib/browser/blueprints/index.mjs +31 -0
  2. package/dist/lib/browser/blueprints/index.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-5LWSJZTZ.mjs +17 -0
  4. package/dist/lib/browser/chunk-5LWSJZTZ.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-HOOPZ7EM.mjs +35 -0
  6. package/dist/lib/browser/chunk-HOOPZ7EM.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-J5LGTIGS.mjs +10 -0
  8. package/dist/lib/browser/chunk-RIUSFL62.mjs +112 -0
  9. package/dist/lib/browser/chunk-RIUSFL62.mjs.map +7 -0
  10. package/dist/lib/browser/create-IZPB7VZW.mjs +19 -0
  11. package/dist/lib/browser/create-IZPB7VZW.mjs.map +7 -0
  12. package/dist/lib/browser/index.mjs +1178 -97
  13. package/dist/lib/browser/index.mjs.map +4 -4
  14. package/dist/lib/browser/meta.json +1 -1
  15. package/dist/lib/browser/open-SN5O5I6X.mjs +29 -0
  16. package/dist/lib/browser/open-SN5O5I6X.mjs.map +7 -0
  17. package/dist/lib/browser/operations/index.mjs +13 -0
  18. package/dist/lib/browser/operations/index.mjs.map +7 -0
  19. package/dist/lib/browser/summarize-5MZENAU5.mjs +71 -0
  20. package/dist/lib/browser/summarize-5MZENAU5.mjs.map +7 -0
  21. package/dist/lib/browser/types/index.mjs +4 -6
  22. package/dist/lib/node-esm/blueprints/index.mjs +32 -0
  23. package/dist/lib/node-esm/blueprints/index.mjs.map +7 -0
  24. package/dist/lib/node-esm/chunk-3GSFEN4L.mjs +113 -0
  25. package/dist/lib/node-esm/chunk-3GSFEN4L.mjs.map +7 -0
  26. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
  27. package/dist/lib/node-esm/chunk-NVM2QUMX.mjs +37 -0
  28. package/dist/lib/node-esm/chunk-NVM2QUMX.mjs.map +7 -0
  29. package/dist/lib/node-esm/chunk-X74WYCCE.mjs +19 -0
  30. package/dist/lib/node-esm/chunk-X74WYCCE.mjs.map +7 -0
  31. package/dist/lib/node-esm/create-TQDGXQBL.mjs +20 -0
  32. package/dist/lib/node-esm/create-TQDGXQBL.mjs.map +7 -0
  33. package/dist/lib/node-esm/index.mjs +1178 -97
  34. package/dist/lib/node-esm/index.mjs.map +4 -4
  35. package/dist/lib/node-esm/meta.json +1 -1
  36. package/dist/lib/node-esm/open-4GAAWWKJ.mjs +30 -0
  37. package/dist/lib/node-esm/open-4GAAWWKJ.mjs.map +7 -0
  38. package/dist/lib/node-esm/operations/index.mjs +14 -0
  39. package/dist/lib/node-esm/operations/index.mjs.map +7 -0
  40. package/dist/lib/node-esm/summarize-RVOSRHEC.mjs +72 -0
  41. package/dist/lib/node-esm/summarize-RVOSRHEC.mjs.map +7 -0
  42. package/dist/lib/node-esm/types/index.mjs +4 -6
  43. package/dist/types/src/TranscriptionPlugin.d.ts +2 -1
  44. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  45. package/dist/types/src/blueprints/index.d.ts +2 -0
  46. package/dist/types/src/blueprints/index.d.ts.map +1 -0
  47. package/dist/types/src/blueprints/transcription-blueprint.d.ts +4 -0
  48. package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -0
  49. package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -4
  50. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
  51. package/dist/types/src/capabilities/index.d.ts +6 -5
  52. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  53. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  54. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  55. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  56. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  57. package/dist/types/src/capabilities/transcriber.d.ts +4 -3
  58. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
  59. package/dist/types/src/components/Transcription/Transcription.d.ts +22 -0
  60. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -0
  61. package/dist/types/src/components/{Transcript/Transcript.stories.d.ts → Transcription/Transcription.stories.d.ts} +13 -12
  62. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -0
  63. package/dist/types/src/components/Transcription/index.d.ts +2 -0
  64. package/dist/types/src/components/Transcription/index.d.ts.map +1 -0
  65. package/dist/types/src/components/{Transcript/transcript-extension.d.ts → Transcription/transcription-extension.d.ts} +3 -3
  66. package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -0
  67. package/dist/types/src/components/index.d.ts +1 -3
  68. package/dist/types/src/components/index.d.ts.map +1 -1
  69. package/dist/types/src/components/stories/FileTranscription.stories.d.ts +3 -3
  70. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +1 -1
  71. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +2 -2
  72. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  73. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +5 -5
  74. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +1 -1
  75. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +1 -1
  76. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +6 -0
  77. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +1 -0
  78. package/dist/types/src/containers/TranscriptionContainer/index.d.ts +2 -0
  79. package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +1 -0
  80. package/dist/types/src/containers/index.d.ts +3 -0
  81. package/dist/types/src/containers/index.d.ts.map +1 -0
  82. package/dist/types/src/hooks/useQueueModelAdapter.d.ts +1 -1
  83. package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
  84. package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
  85. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  86. package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
  87. package/dist/types/src/index.d.ts +1 -1
  88. package/dist/types/src/index.d.ts.map +1 -1
  89. package/dist/types/src/meta.d.ts +2 -2
  90. package/dist/types/src/meta.d.ts.map +1 -1
  91. package/dist/types/src/operations/create.d.ts +5 -0
  92. package/dist/types/src/operations/create.d.ts.map +1 -0
  93. package/dist/types/src/operations/definitions.d.ts +905 -0
  94. package/dist/types/src/operations/definitions.d.ts.map +1 -0
  95. package/dist/types/src/operations/index.d.ts +4 -0
  96. package/dist/types/src/operations/index.d.ts.map +1 -0
  97. package/dist/types/src/operations/open.d.ts +5 -0
  98. package/dist/types/src/operations/open.d.ts.map +1 -0
  99. package/dist/types/src/operations/summarize.d.ts +8 -0
  100. package/dist/types/src/operations/summarize.d.ts.map +1 -0
  101. package/dist/types/src/segments-normalization/message-normalizer.d.ts +2 -2
  102. package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +1 -1
  103. package/dist/types/src/segments-normalization/normalization.d.ts +360 -474
  104. package/dist/types/src/segments-normalization/normalization.d.ts.map +1 -1
  105. package/dist/types/src/testing/testing.d.ts +4 -4
  106. package/dist/types/src/testing/testing.d.ts.map +1 -1
  107. package/dist/types/src/transcriber/media-stream-recorder.d.ts +2 -2
  108. package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
  109. package/dist/types/src/transcriber/transcriber.d.ts +2 -2
  110. package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
  111. package/dist/types/src/transcriber/transcription-manager.d.ts +6 -3
  112. package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
  113. package/dist/types/src/translations.d.ts +7 -7
  114. package/dist/types/src/translations.d.ts.map +1 -1
  115. package/dist/types/src/types/capabilities.d.ts +18 -0
  116. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  117. package/dist/types/src/types/index.d.ts +1 -2
  118. package/dist/types/src/types/index.d.ts.map +1 -1
  119. package/dist/types/src/util/index.d.ts +1 -0
  120. package/dist/types/src/util/index.d.ts.map +1 -1
  121. package/dist/types/src/util/render-byline.d.ts +4 -0
  122. package/dist/types/src/util/render-byline.d.ts.map +1 -0
  123. package/dist/types/tsconfig.tsbuildinfo +1 -1
  124. package/package.json +105 -72
  125. package/src/TranscriptionPlugin.tsx +40 -60
  126. package/src/blueprints/index.ts +5 -0
  127. package/src/blueprints/transcription-blueprint.ts +30 -0
  128. package/src/capabilities/blueprint-definition.ts +10 -24
  129. package/src/capabilities/index.ts +9 -7
  130. package/src/capabilities/operation-handler.ts +16 -0
  131. package/src/capabilities/react-surface.tsx +21 -14
  132. package/src/capabilities/transcriber.ts +52 -43
  133. package/src/components/{Transcript/Transcript.stories.tsx → Transcription/Transcription.stories.tsx} +52 -48
  134. package/src/components/Transcription/Transcription.tsx +64 -0
  135. package/src/components/{Transcript → Transcription}/index.ts +1 -1
  136. package/src/components/{Transcript/transcript-extension.ts → Transcription/transcription-extension.ts} +4 -4
  137. package/src/components/index.ts +1 -6
  138. package/src/components/stories/FileTranscription.stories.tsx +26 -33
  139. package/src/components/stories/KeyWordRecognition.stories.tsx +7 -7
  140. package/src/components/stories/MicrophoneTranscription.stories.tsx +50 -72
  141. package/src/components/stories/TranscriptionStory.tsx +7 -8
  142. package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +33 -0
  143. package/src/containers/TranscriptionContainer/index.ts +5 -0
  144. package/src/containers/index.ts +7 -0
  145. package/src/hooks/useQueueModelAdapter.ts +1 -1
  146. package/src/hooks/useTranscriber.ts +3 -2
  147. package/src/hooks/useVoiceInput.ts +2 -3
  148. package/src/index.ts +1 -1
  149. package/src/meta.ts +3 -3
  150. package/src/model/model.test.ts +9 -3
  151. package/src/operations/create.ts +22 -0
  152. package/src/operations/definitions.ts +102 -0
  153. package/src/operations/index.ts +13 -0
  154. package/src/operations/open.ts +31 -0
  155. package/src/{functions → operations}/summarize.ts +41 -47
  156. package/src/segments-normalization/message-normalizer.ts +12 -7
  157. package/src/segments-normalization/normalization.test.ts +2 -7
  158. package/src/segments-normalization/normalization.ts +33 -71
  159. package/src/testing/testing.ts +17 -22
  160. package/src/transcriber/media-stream-recorder.ts +3 -3
  161. package/src/transcriber/transcriber.browser.test.ts +1 -2
  162. package/src/transcriber/transcriber.ts +5 -5
  163. package/src/transcriber/transcription-manager.ts +15 -9
  164. package/src/translations.ts +7 -7
  165. package/src/{capabilities → types}/capabilities.ts +11 -10
  166. package/src/types/index.ts +1 -2
  167. package/src/util/index.ts +1 -0
  168. package/src/util/render-byline.ts +33 -0
  169. package/dist/lib/browser/TranscriptContainer-EKSP5DQ6.mjs +0 -11
  170. package/dist/lib/browser/blueprint-definition-WBWHO4OH.mjs +0 -150
  171. package/dist/lib/browser/blueprint-definition-WBWHO4OH.mjs.map +0 -7
  172. package/dist/lib/browser/chunk-3MKZ2MNQ.mjs +0 -16
  173. package/dist/lib/browser/chunk-3MKZ2MNQ.mjs.map +0 -7
  174. package/dist/lib/browser/chunk-EHT3TE6U.mjs +0 -738
  175. package/dist/lib/browser/chunk-EHT3TE6U.mjs.map +0 -7
  176. package/dist/lib/browser/chunk-OCI42DWB.mjs +0 -8
  177. package/dist/lib/browser/chunk-OCI42DWB.mjs.map +0 -7
  178. package/dist/lib/browser/chunk-PVU4XCKI.mjs +0 -466
  179. package/dist/lib/browser/chunk-PVU4XCKI.mjs.map +0 -7
  180. package/dist/lib/browser/chunk-VBUIO677.mjs +0 -29
  181. package/dist/lib/browser/chunk-VBUIO677.mjs.map +0 -7
  182. package/dist/lib/browser/chunk-W7WKZTM4.mjs +0 -63
  183. package/dist/lib/browser/chunk-W7WKZTM4.mjs.map +0 -7
  184. package/dist/lib/browser/intent-resolver-ZTKTEAQX.mjs +0 -25
  185. package/dist/lib/browser/intent-resolver-ZTKTEAQX.mjs.map +0 -7
  186. package/dist/lib/browser/react-surface-NARMYO2O.mjs +0 -34
  187. package/dist/lib/browser/react-surface-NARMYO2O.mjs.map +0 -7
  188. package/dist/lib/browser/transcriber-E2KAOXPS.mjs +0 -56
  189. package/dist/lib/browser/transcriber-E2KAOXPS.mjs.map +0 -7
  190. package/dist/lib/node-esm/TranscriptContainer-WU3EDQR3.mjs +0 -12
  191. package/dist/lib/node-esm/blueprint-definition-WUHZFRWZ.mjs +0 -151
  192. package/dist/lib/node-esm/blueprint-definition-WUHZFRWZ.mjs.map +0 -7
  193. package/dist/lib/node-esm/chunk-7XASA7DX.mjs +0 -739
  194. package/dist/lib/node-esm/chunk-7XASA7DX.mjs.map +0 -7
  195. package/dist/lib/node-esm/chunk-OLMSTF3M.mjs +0 -64
  196. package/dist/lib/node-esm/chunk-OLMSTF3M.mjs.map +0 -7
  197. package/dist/lib/node-esm/chunk-SWUVPFKR.mjs +0 -17
  198. package/dist/lib/node-esm/chunk-SWUVPFKR.mjs.map +0 -7
  199. package/dist/lib/node-esm/chunk-UVOYXXDJ.mjs +0 -467
  200. package/dist/lib/node-esm/chunk-UVOYXXDJ.mjs.map +0 -7
  201. package/dist/lib/node-esm/chunk-X5BKN32O.mjs +0 -10
  202. package/dist/lib/node-esm/chunk-X5BKN32O.mjs.map +0 -7
  203. package/dist/lib/node-esm/chunk-YVNQN2J6.mjs +0 -30
  204. package/dist/lib/node-esm/chunk-YVNQN2J6.mjs.map +0 -7
  205. package/dist/lib/node-esm/intent-resolver-55WH2GY6.mjs +0 -26
  206. package/dist/lib/node-esm/intent-resolver-55WH2GY6.mjs.map +0 -7
  207. package/dist/lib/node-esm/react-surface-5MQA3IUS.mjs +0 -35
  208. package/dist/lib/node-esm/react-surface-5MQA3IUS.mjs.map +0 -7
  209. package/dist/lib/node-esm/transcriber-ZPJM4U2W.mjs +0 -57
  210. package/dist/lib/node-esm/transcriber-ZPJM4U2W.mjs.map +0 -7
  211. package/dist/types/src/capabilities/capabilities.d.ts +0 -17
  212. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  213. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  214. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  215. package/dist/types/src/components/Transcript/Transcript.d.ts +0 -20
  216. package/dist/types/src/components/Transcript/Transcript.d.ts.map +0 -1
  217. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +0 -1
  218. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +0 -28
  219. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +0 -1
  220. package/dist/types/src/components/Transcript/index.d.ts +0 -2
  221. package/dist/types/src/components/Transcript/index.d.ts.map +0 -1
  222. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +0 -1
  223. package/dist/types/src/components/TranscriptContainer.d.ts +0 -9
  224. package/dist/types/src/components/TranscriptContainer.d.ts.map +0 -1
  225. package/dist/types/src/functions/index.d.ts +0 -3
  226. package/dist/types/src/functions/index.d.ts.map +0 -1
  227. package/dist/types/src/functions/open.d.ts +0 -7
  228. package/dist/types/src/functions/open.d.ts.map +0 -1
  229. package/dist/types/src/functions/summarize.d.ts +0 -11
  230. package/dist/types/src/functions/summarize.d.ts.map +0 -1
  231. package/dist/types/src/types/Transcript.d.ts +0 -30
  232. package/dist/types/src/types/Transcript.d.ts.map +0 -1
  233. package/dist/types/src/types/TranscriptAction.d.ts +0 -23
  234. package/dist/types/src/types/TranscriptAction.d.ts.map +0 -1
  235. package/src/capabilities/intent-resolver.ts +0 -18
  236. package/src/components/Transcript/Transcript.tsx +0 -94
  237. package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
  238. package/src/components/TranscriptContainer.tsx +0 -37
  239. package/src/functions/index.ts +0 -6
  240. package/src/functions/open.ts +0 -39
  241. package/src/types/Transcript.ts +0 -44
  242. package/src/types/TranscriptAction.ts +0 -24
  243. /package/dist/lib/browser/{TranscriptContainer-EKSP5DQ6.mjs.map → chunk-J5LGTIGS.mjs.map} +0 -0
  244. /package/dist/lib/node-esm/{TranscriptContainer-WU3EDQR3.mjs.map → chunk-HSLMI22Q.mjs.map} +0 -0
@@ -1,122 +1,1203 @@
1
- import {
2
- TranscriptContainer
3
- } from "./chunk-OCI42DWB.mjs";
4
- import {
5
- BlueprintDefinition,
6
- IntentResolver,
7
- ReactSurface,
8
- Transcriber as Transcriber2,
9
- TranscriptView,
10
- TranscriptionContainer,
11
- renderByline,
12
- useAudioFile,
13
- useAudioTrack,
14
- useQueueModelAdapter,
15
- useTranscriber,
16
- useVoiceInput
17
- } from "./chunk-EHT3TE6U.mjs";
18
- import {
19
- MediaStreamRecorder,
20
- Transcriber,
21
- TranscriptionManager
22
- } from "./chunk-PVU4XCKI.mjs";
23
1
  import {
24
2
  TranscriptionCapabilities
25
- } from "./chunk-3MKZ2MNQ.mjs";
3
+ } from "./chunk-5LWSJZTZ.mjs";
26
4
  import {
27
- Transcript_exports
28
- } from "./chunk-W7WKZTM4.mjs";
29
- import {
30
- meta
31
- } from "./chunk-VBUIO677.mjs";
5
+ mergeFloat64Arrays,
6
+ renderByline
7
+ } from "./chunk-HOOPZ7EM.mjs";
8
+ import "./chunk-J5LGTIGS.mjs";
32
9
 
33
- // src/TranscriptionPlugin.tsx
34
- import { Capabilities, Events, contributes, defineModule, definePlugin } from "@dxos/app-framework";
10
+ // src/components/Transcription/Transcription.tsx
11
+ import React from "react";
12
+ import { useThemeContext } from "@dxos/react-ui";
13
+ import { useTextEditor } from "@dxos/react-ui-editor";
14
+ import { autoScroll, createBasicExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown, documentSlots, preview } from "@dxos/ui-editor";
15
+ import { composable, composableProps } from "@dxos/ui-theme";
16
+ import { isTruthy } from "@dxos/util";
17
+
18
+ // src/components/Transcription/transcription-extension.ts
19
+ import { RangeSetBuilder } from "@codemirror/state";
20
+ import { EditorView, GutterMarker, ViewPlugin, gutter } from "@codemirror/view";
21
+ import { format } from "date-fns/format";
22
+ import { intervalToDuration } from "date-fns/intervalToDuration";
23
+ import { addEventListener, combine } from "@dxos/async";
24
+
25
+ // src/model/model.ts
26
+ import { Text } from "@codemirror/state";
27
+ import { Event } from "@dxos/async";
28
+ import { invariant } from "@dxos/invariant";
29
+ import { log } from "@dxos/log";
30
+ var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/model/model.ts";
31
+ var DocumentAdapter = class {
32
+ _view;
33
+ constructor(_view) {
34
+ this._view = _view;
35
+ }
36
+ /** The document must always have at least one line. */
37
+ lineCount() {
38
+ return this._view.state.doc.lines - 1;
39
+ }
40
+ replaceLines(from, remove, insert) {
41
+ log("replaceLines", {
42
+ count: this._view.state.doc.lines,
43
+ from,
44
+ remove,
45
+ insert
46
+ }, {
47
+ F: __dxlog_file,
48
+ L: 39,
49
+ S: this,
50
+ C: (f, a) => f(...a)
51
+ });
52
+ const numLines = this._view.state.doc.lines;
53
+ const posFrom = this._view.state.doc.line(from).from;
54
+ const posTo = remove === 0 ? posFrom : this._view.state.doc.line(Math.min(numLines, from + remove)).from;
55
+ let text = insert.join("\n");
56
+ if (posTo === this._view.state.doc.length) {
57
+ text += "\n";
58
+ }
59
+ this._view.dispatch({
60
+ changes: {
61
+ from: posFrom,
62
+ to: posTo,
63
+ insert: text
64
+ }
65
+ });
66
+ }
67
+ };
68
+ var SerializationModel = class {
69
+ _renderer;
70
+ /** Ordered set of chunks. */
71
+ _chunks = [];
72
+ /** Map of chunk IDs to their current line counts. */
73
+ _chunkLineCounts = /* @__PURE__ */ new Map();
74
+ /** Map of line numbers to chunk IDs. */
75
+ _lineToChunk = /* @__PURE__ */ new Map();
76
+ /** Track chunk changes since last sync. */
77
+ _changes = [];
78
+ /** Emits when the document is updated. */
79
+ update = new Event();
80
+ constructor(_renderer, initialChunks = []) {
81
+ this._renderer = _renderer;
82
+ initialChunks.forEach((chunk) => {
83
+ this.appendChunk(chunk);
84
+ });
85
+ }
86
+ toJSON() {
87
+ return {
88
+ chunks: this._chunks.length,
89
+ changes: this._changes.length
90
+ };
91
+ }
92
+ get doc() {
93
+ return Text.of([
94
+ ...this._chunks.flatMap((chunk, index) => this._renderer(chunk, index)),
95
+ ""
96
+ ]);
97
+ }
98
+ get chunks() {
99
+ return this._chunks;
100
+ }
101
+ /**
102
+ * Get the block ID at the given line number.
103
+ */
104
+ getChunkAtLine(line) {
105
+ return this._lineToChunk.get(line);
106
+ }
107
+ reset() {
108
+ this._chunks.length = 0;
109
+ this._chunkLineCounts.clear();
110
+ this._lineToChunk.clear();
111
+ this.update.emit();
112
+ return this;
113
+ }
114
+ appendChunk(chunk) {
115
+ const index = this._chunks.length;
116
+ this._chunks.push(chunk);
117
+ this._changes.push({
118
+ type: "append",
119
+ index,
120
+ chunk
121
+ });
122
+ this.update.emit();
123
+ return this;
124
+ }
125
+ updateChunk(chunk) {
126
+ const index = this._chunks.findIndex((c) => c.id === chunk.id);
127
+ invariant(index !== -1, void 0, {
128
+ F: __dxlog_file,
129
+ L: 147,
130
+ S: this,
131
+ A: [
132
+ "index !== -1",
133
+ ""
134
+ ]
135
+ });
136
+ this._chunks[index] = chunk;
137
+ this._changes.push({
138
+ type: "update",
139
+ index,
140
+ chunk
141
+ });
142
+ this.update.emit();
143
+ return this;
144
+ }
145
+ deleteBlock(id) {
146
+ const index = this._chunks.findIndex((c) => c.id === id);
147
+ invariant(index !== -1, void 0, {
148
+ F: __dxlog_file,
149
+ L: 156,
150
+ S: this,
151
+ A: [
152
+ "index !== -1",
153
+ ""
154
+ ]
155
+ });
156
+ this._chunks.splice(index, 1);
157
+ this._changes.push({
158
+ type: "delete",
159
+ index,
160
+ id
161
+ });
162
+ this.update.emit();
163
+ return this;
164
+ }
165
+ /**
166
+ * Syncs the tracked changes with the ChunkDocument.
167
+ */
168
+ sync(document2) {
169
+ log("sync", {
170
+ changes: this._changes
171
+ }, {
172
+ F: __dxlog_file,
173
+ L: 167,
174
+ S: this,
175
+ C: (f, a) => f(...a)
176
+ });
177
+ for (const change of this._changes) {
178
+ switch (change.type) {
179
+ case "append": {
180
+ const lines = this._renderer(change.chunk, change.index);
181
+ const lastLine = document2.lineCount();
182
+ document2.replaceLines(lastLine + 1, 0, lines);
183
+ break;
184
+ }
185
+ case "update": {
186
+ let lineStart = 1;
187
+ for (let i = 0; i < change.index; i++) {
188
+ lineStart += this._chunkLineCounts.get(this._chunks[i].id);
189
+ }
190
+ const previousLineCount = this._chunkLineCounts.get(change.chunk.id);
191
+ const newLines = this._renderer(change.chunk, change.index);
192
+ document2.replaceLines(lineStart, previousLineCount, newLines);
193
+ break;
194
+ }
195
+ case "delete": {
196
+ let lineStart = 1;
197
+ for (let i = 0; i < change.index; i++) {
198
+ lineStart += this._chunkLineCounts.get(this._chunks[i].id);
199
+ }
200
+ const previousLineCount = this._chunkLineCounts.get(change.id);
201
+ document2.replaceLines(lineStart, previousLineCount, []);
202
+ break;
203
+ }
204
+ }
205
+ }
206
+ this._lineToChunk.clear();
207
+ let currentLine = 1;
208
+ for (const [index, chunk] of this._chunks.entries()) {
209
+ const lineCount = this._renderer(chunk, index).length;
210
+ this._chunkLineCounts.set(chunk.id, lineCount);
211
+ this._lineToChunk.set(currentLine, chunk);
212
+ currentLine += lineCount;
213
+ }
214
+ const chunkIds = new Set(this._chunks.map((chunk) => chunk.id));
215
+ for (const id of this._chunkLineCounts.keys()) {
216
+ if (!chunkIds.has(id)) {
217
+ this._chunkLineCounts.delete(id);
218
+ }
219
+ }
220
+ this._changes.length = 0;
221
+ return this;
222
+ }
223
+ };
224
+
225
+ // src/components/Transcription/transcription-extension.ts
226
+ var transcription = ({ model, started }) => {
227
+ return [
228
+ // Show timestamps in the gutter.
229
+ gutter({
230
+ class: "cm-timestamp-gutter",
231
+ lineMarkerChange: (update) => update.docChanged || update.viewportChanged,
232
+ markers: (view) => {
233
+ const start = getStartTime(started, model.chunks[0]);
234
+ const builder = new RangeSetBuilder();
235
+ for (const { from, to } of view.visibleRanges) {
236
+ let line = view.state.doc.lineAt(from);
237
+ while (line.from <= to) {
238
+ const block = model.getChunkAtLine(line.number)?.blocks[0];
239
+ const timestamp = block?._tag === "transcript" && block.started;
240
+ if (timestamp) {
241
+ builder.add(line.from, line.from, new TimestampMarker(line.number, new Date(timestamp), start));
242
+ }
243
+ if (line.to + 1 > view.state.doc.length) {
244
+ break;
245
+ }
246
+ line = view.state.doc.lineAt(line.to + 1);
247
+ }
248
+ }
249
+ return builder.finish();
250
+ }
251
+ }),
252
+ // Listen for model updates.
253
+ ViewPlugin.fromClass(class {
254
+ _controls;
255
+ _cleanup;
256
+ _adapter;
257
+ _initialized = false;
258
+ constructor(view) {
259
+ this._adapter = new DocumentAdapter(view);
260
+ const scroller = view.scrollDOM;
261
+ let isAutoScrolling = false;
262
+ let hasScrolled = false;
263
+ let timeout;
264
+ const scrollToBottom = (smooth = false) => {
265
+ scroller.style.scrollBehavior = smooth ? "smooth" : "";
266
+ scroller.classList.add("cm-hide-scrollbar");
267
+ isAutoScrolling = true;
268
+ clearTimeout(timeout);
269
+ timeout = setTimeout(() => {
270
+ this._controls?.classList.add("opacity-0");
271
+ scroller.classList.remove("cm-hide-scrollbar");
272
+ isAutoScrolling = false;
273
+ }, 500);
274
+ view.dispatch({
275
+ effects: EditorView.scrollIntoView(view.state.doc.length, {
276
+ y: "end"
277
+ })
278
+ });
279
+ };
280
+ this._cleanup = combine(addEventListener(view.scrollDOM, "scroll", () => {
281
+ if (!isAutoScrolling) {
282
+ hasScrolled = true;
283
+ this._controls?.classList.remove("opacity-0");
284
+ }
285
+ }), model.update.on(() => {
286
+ const autoScroll2 = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight === 0 || !hasScrolled;
287
+ model.sync(this._adapter);
288
+ if (autoScroll2) {
289
+ scrollToBottom(true);
290
+ }
291
+ }));
292
+ }
293
+ update(_update) {
294
+ if (!this._initialized) {
295
+ this._initialized = true;
296
+ setTimeout(() => {
297
+ model.sync(this._adapter);
298
+ });
299
+ }
300
+ }
301
+ destroy() {
302
+ this._controls?.remove();
303
+ this._cleanup();
304
+ }
305
+ }),
306
+ EditorView.theme({
307
+ ".cm-gutters": {
308
+ backgroundColor: "var(--color-base-surface)"
309
+ },
310
+ ".cm-timestamp-gutter": {
311
+ width: "6rem",
312
+ paddingLeft: "0.5rem",
313
+ paddingRight: "1rem"
314
+ },
315
+ ".cm-timestamp-gutter > .cm-gutterElement > div": {
316
+ display: "inline-flex",
317
+ textAlign: "right",
318
+ padding: "3px"
319
+ }
320
+ })
321
+ ];
322
+ };
323
+ var TimestampMarker = class extends GutterMarker {
324
+ _line;
325
+ _timestamp;
326
+ _started;
327
+ constructor(_line, _timestamp, _started) {
328
+ super(), this._line = _line, this._timestamp = _timestamp, this._started = _started;
329
+ }
330
+ eq(other) {
331
+ return this._timestamp === other._timestamp;
332
+ }
333
+ toDOM(view) {
334
+ const el = document.createElement("div");
335
+ el.className = "text-sm text-subdued hover:bg-hover-surface cursor-pointer";
336
+ el.textContent = formatTimestamp(this._timestamp, this._started);
337
+ el.onclick = () => {
338
+ const pos = view.state.doc.line(this._line).from;
339
+ view.dispatch({
340
+ effects: EditorView.scrollIntoView(pos, {
341
+ y: "start"
342
+ })
343
+ });
344
+ };
345
+ return el;
346
+ }
347
+ };
348
+ var getStartTime = (started, message) => {
349
+ if (started) {
350
+ return started;
351
+ }
352
+ if (message?.blocks[0]?._tag === "transcript" && message.blocks[0].started) {
353
+ return new Date(message.blocks[0].started);
354
+ }
355
+ return void 0;
356
+ };
357
+ var formatTimestamp = (timestamp, relative) => {
358
+ if (relative) {
359
+ const pad = (n = 0) => String(n).padStart(2, "0");
360
+ const { hours, minutes, seconds } = intervalToDuration({
361
+ start: relative,
362
+ end: timestamp
363
+ });
364
+ return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
365
+ } else {
366
+ return format(timestamp, "HH:mm:ss");
367
+ }
368
+ };
369
+
370
+ // src/components/Transcription/Transcription.tsx
371
+ var Transcription = composable(({ transcript: object, model, children, ...props }, forwardedRef) => {
372
+ const { themeMode } = useThemeContext();
373
+ const { parentRef } = useTextEditor(() => {
374
+ return {
375
+ extensions: [
376
+ createBasicExtensions({
377
+ readOnly: true,
378
+ lineWrapping: true,
379
+ search: true
380
+ }),
381
+ createMarkdownExtensions(),
382
+ createThemeExtensions({
383
+ themeMode,
384
+ slots: documentSlots
385
+ }),
386
+ decorateMarkdown(),
387
+ preview(),
388
+ transcription({
389
+ model,
390
+ started: object?.started ? new Date(object.started) : void 0
391
+ }),
392
+ autoScroll()
393
+ ].filter(isTruthy)
394
+ };
395
+ }, [
396
+ model
397
+ ]);
398
+ return /* @__PURE__ */ React.createElement("div", {
399
+ ...composableProps(props, {
400
+ classNames: "dx-container"
401
+ }),
402
+ ref: parentRef,
403
+ "data-popover-collision-boundary": true
404
+ });
405
+ });
406
+
407
+ // src/hooks/useAudioFile.ts
408
+ import { useEffect, useState } from "react";
409
+ import { scheduleTask } from "@dxos/async";
410
+ import { Context } from "@dxos/context";
411
+ import { log as log2 } from "@dxos/log";
412
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/hooks/useAudioFile.ts";
413
+ var useAudioFile = (audioUrl, constraints) => {
414
+ const [{ audio, stream, track }, setStream] = useState({});
415
+ useEffect(() => {
416
+ const ctx = new Context(void 0, {
417
+ F: __dxlog_file2,
418
+ L: 20
419
+ });
420
+ scheduleTask(ctx, async () => {
421
+ try {
422
+ const response = await fetch(audioUrl);
423
+ const blob = await response.blob();
424
+ const objectUrl = URL.createObjectURL(blob);
425
+ ctx.onDispose(() => {
426
+ URL.revokeObjectURL(objectUrl);
427
+ });
428
+ const audio2 = new Audio();
429
+ audio2.src = objectUrl;
430
+ await new Promise((resolve, reject) => {
431
+ audio2.addEventListener("error", (err) => {
432
+ log2.error("error", {
433
+ err
434
+ }, {
435
+ F: __dxlog_file2,
436
+ L: 39,
437
+ S: void 0,
438
+ C: (f, a) => f(...a)
439
+ });
440
+ reject(err);
441
+ }, {
442
+ once: true
443
+ });
444
+ audio2.addEventListener("canplay", async () => {
445
+ resolve();
446
+ }, {
447
+ once: true
448
+ });
449
+ audio2.load();
450
+ });
451
+ const audioCtx = new AudioContext();
452
+ if (audioCtx.state === "suspended") {
453
+ await audioCtx.resume();
454
+ }
455
+ const destination = audioCtx.createMediaStreamDestination();
456
+ destination.channelCount = 1;
457
+ const source = audioCtx.createMediaElementSource(audio2);
458
+ source.connect(destination);
459
+ const track2 = destination.stream.getAudioTracks()[0];
460
+ if (constraints) {
461
+ await track2.applyConstraints(constraints).catch((err) => log2.catch(err, void 0, {
462
+ F: __dxlog_file2,
463
+ L: 68,
464
+ S: void 0,
465
+ C: (f, a) => f(...a)
466
+ }));
467
+ }
468
+ setStream({
469
+ audio: audio2,
470
+ stream: destination.stream,
471
+ track: destination.stream.getAudioTracks()[0]
472
+ });
473
+ } catch (err) {
474
+ log2.catch(err, void 0, {
475
+ F: __dxlog_file2,
476
+ L: 78,
477
+ S: void 0,
478
+ C: (f, a) => f(...a)
479
+ });
480
+ }
481
+ });
482
+ return () => {
483
+ void ctx.dispose();
484
+ };
485
+ }, [
486
+ audioUrl
487
+ ]);
488
+ return {
489
+ audio,
490
+ stream,
491
+ track
492
+ };
493
+ };
494
+
495
+ // src/hooks/useAudioTrack.ts
496
+ import { useEffect as useEffect2, useState as useState2 } from "react";
497
+ var useAudioTrack = (active, constraints) => {
498
+ const [track, setTrack] = useState2();
499
+ useEffect2(() => {
500
+ if (!active) {
501
+ track?.stop();
502
+ setTrack(void 0);
503
+ return;
504
+ }
505
+ const initAudio = async () => {
506
+ const audio = new Audio();
507
+ audio.srcObject = await navigator.mediaDevices.getUserMedia({
508
+ audio: constraints ?? true
509
+ });
510
+ const [track2] = audio.srcObject.getAudioTracks();
511
+ if (track2) {
512
+ setTrack(track2);
513
+ }
514
+ };
515
+ if (active) {
516
+ void initAudio();
517
+ }
518
+ }, [
519
+ active
520
+ ]);
521
+ return track;
522
+ };
523
+
524
+ // src/hooks/useQueueModelAdapter.ts
525
+ import { useEffect as useEffect3, useMemo, useState as useState3 } from "react";
526
+ var useQueueModelAdapter = (renderer, queue, initialChunks = []) => {
527
+ const model = useMemo(() => new SerializationModel(renderer, initialChunks), [
528
+ queue
529
+ ]);
530
+ const [loaded, setLoaded] = useState3(false);
531
+ useEffect3(() => {
532
+ if (!queue) {
533
+ return;
534
+ }
535
+ const update = () => {
536
+ for (const block of queue?.objects ?? []) {
537
+ model.appendChunk(block);
538
+ }
539
+ setLoaded(true);
540
+ };
541
+ let i;
542
+ if (queue.isLoading) {
543
+ i = setInterval(() => {
544
+ if (!queue?.isLoading) {
545
+ clearInterval(i);
546
+ update();
547
+ }
548
+ }, 1e3);
549
+ } else {
550
+ update();
551
+ }
552
+ return () => clearTimeout(i);
553
+ }, [
554
+ model,
555
+ queue
556
+ ]);
557
+ useEffect3(() => {
558
+ if (!loaded || !queue || model.chunks.length === queue.objects.length) {
559
+ return;
560
+ }
561
+ const chunk = queue.objects.at(-1);
562
+ if (chunk) {
563
+ model.appendChunk(chunk);
564
+ }
565
+ }, [
566
+ model,
567
+ loaded,
568
+ queue?.objects.length
569
+ ]);
570
+ return model;
571
+ };
572
+
573
+ // src/hooks/useTranscriber.ts
574
+ import { useEffect as useEffect4, useMemo as useMemo2 } from "react";
575
+ import { useCapabilities } from "@dxos/app-framework/ui";
576
+ import { TranscriptionCapabilities as TranscriptionCapabilities2 } from "#types";
577
+ var useTranscriber = ({ audioStreamTrack, recorderConfig, transcriberConfig, onSegments }) => {
578
+ const [getTranscriber] = useCapabilities(TranscriptionCapabilities2.Transcriber);
579
+ const transcriber = useMemo2(() => {
580
+ if (!getTranscriber || !audioStreamTrack || !onSegments) {
581
+ return void 0;
582
+ }
583
+ return getTranscriber({
584
+ audioStreamTrack,
585
+ recorderConfig,
586
+ transcriberConfig,
587
+ onSegments
588
+ });
589
+ }, [
590
+ getTranscriber,
591
+ audioStreamTrack,
592
+ recorderConfig,
593
+ transcriberConfig,
594
+ onSegments
595
+ ]);
596
+ useEffect4(() => {
597
+ return () => {
598
+ void transcriber?.close();
599
+ };
600
+ }, [
601
+ transcriber
602
+ ]);
603
+ return transcriber;
604
+ };
605
+
606
+ // src/hooks/useVoiceInput.ts
607
+ import { useCallback, useEffect as useEffect5, useState as useState4 } from "react";
608
+ import { scheduleMicroTask } from "@dxos/async";
609
+ import { Context as Context2 } from "@dxos/context";
610
+ import { log as log3 } from "@dxos/log";
611
+ import { useSoundEffect } from "@dxos/react-ui-sfx";
612
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/hooks/useVoiceInput.ts";
613
+ var useVoiceInput = ({ active, onUpdate }) => {
614
+ const soundStart = useSoundEffect("StartRecording");
615
+ const soundStop = useSoundEffect("StopRecording");
616
+ const handleSegments = useCallback(async (segments) => {
617
+ const text = segments.map((str) => str.text.trim().replace(/[^\w\s]/g, "")).join(" ");
618
+ onUpdate(text);
619
+ }, []);
620
+ const [recording, setRecording] = useState4(false);
621
+ const track = useAudioTrack(active);
622
+ const transcriber = useTranscriber({
623
+ audioStreamTrack: track,
624
+ onSegments: handleSegments
625
+ });
626
+ useEffect5(() => {
627
+ const ctx = new Context2(void 0, {
628
+ F: __dxlog_file3,
629
+ L: 43
630
+ });
631
+ scheduleMicroTask(ctx, async () => {
632
+ if (active && transcriber) {
633
+ await transcriber.open();
634
+ log3.info("starting...", void 0, {
635
+ F: __dxlog_file3,
636
+ L: 47,
637
+ S: void 0,
638
+ C: (f, a) => f(...a)
639
+ });
640
+ setRecording(true);
641
+ void soundStart.play();
642
+ transcriber.startChunksRecording();
643
+ } else if (!active && transcriber?.isOpen) {
644
+ transcriber?.stopChunksRecording();
645
+ await transcriber?.close();
646
+ log3.info("stopped", void 0, {
647
+ F: __dxlog_file3,
648
+ L: 54,
649
+ S: void 0,
650
+ C: (f, a) => f(...a)
651
+ });
652
+ setRecording(false);
653
+ void soundStop.play();
654
+ }
655
+ });
656
+ return () => {
657
+ void ctx.dispose();
658
+ };
659
+ }, [
660
+ active,
661
+ transcriber
662
+ ]);
663
+ return {
664
+ recording
665
+ };
666
+ };
667
+
668
+ // src/meta.ts
669
+ import { trim } from "@dxos/util";
670
+ var meta = {
671
+ id: "org.dxos.plugin.transcription",
672
+ name: "Transcription",
673
+ description: trim`
674
+ Real-time voice-to-text transcription service for capturing spoken content.
675
+ Convert audio input into searchable text that integrates seamlessly with notes and documents.
676
+ `,
677
+ icon: "ph--microphone--regular",
678
+ iconHue: "sky",
679
+ source: "https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-transcription",
680
+ tags: [
681
+ "labs"
682
+ ],
683
+ screenshots: []
684
+ };
685
+
686
+ // src/transcriber/media-stream-recorder.ts
687
+ import { MediaRecorder, register } from "extendable-media-recorder";
688
+ import { WaveFile } from "wavefile";
689
+ import { synchronized } from "@dxos/async";
690
+ import { invariant as invariant2 } from "@dxos/invariant";
691
+ import { log as log4 } from "@dxos/log";
692
+ import { trace } from "@dxos/tracing";
693
+ function _ts_decorate(decorators, target, key, desc) {
694
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
695
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
696
+ 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;
697
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
698
+ }
699
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/transcriber/media-stream-recorder.ts";
700
+ var initializingPromise;
701
+ var initializeExtendableMediaRecorder = async () => {
702
+ const { connect } = await import("extendable-media-recorder-wav-encoder");
703
+ await register(await connect());
704
+ };
705
+ var MediaStreamRecorder = class {
706
+ _mediaStreamTrack;
707
+ /**
708
+ * Default MediaRecorder implementation do not support wav encoding.
709
+ */
710
+ _config;
711
+ _mediaRecorder = void 0;
712
+ _header = void 0;
713
+ _onChunk;
714
+ constructor({ mediaStreamTrack, config }) {
715
+ this._mediaStreamTrack = mediaStreamTrack;
716
+ this._config = config;
717
+ }
718
+ get wavConfig() {
719
+ const settings = this._mediaStreamTrack.getSettings();
720
+ return {
721
+ channels: settings.channelCount ?? 1,
722
+ sampleRate: settings.sampleRate ?? 16e3,
723
+ bitDepthCode: settings.sampleSize ? String(settings.sampleSize) : "16"
724
+ };
725
+ }
726
+ get mediaStreamTrack() {
727
+ return this._mediaStreamTrack;
728
+ }
729
+ setOnChunk(onChunk) {
730
+ this._onChunk = onChunk;
731
+ }
732
+ async start() {
733
+ await (initializingPromise ??= initializeExtendableMediaRecorder()).catch((err) => log4.info("initializeExtendableMediaRecorder", {
734
+ err
735
+ }, {
736
+ F: __dxlog_file4,
737
+ L: 69,
738
+ S: this,
739
+ C: (f, a) => f(...a)
740
+ }));
741
+ if (!this._mediaRecorder) {
742
+ const stream = new MediaStream([
743
+ this._mediaStreamTrack
744
+ ]);
745
+ this._mediaRecorder = new MediaRecorder(stream, {
746
+ mimeType: "audio/wav"
747
+ });
748
+ }
749
+ if (this._mediaRecorder.state === "recording") {
750
+ return;
751
+ }
752
+ invariant2(this._onChunk, "MediaStreamRecorder: onChunk is not set", {
753
+ F: __dxlog_file4,
754
+ L: 80,
755
+ S: this,
756
+ A: [
757
+ "this._onChunk",
758
+ "'MediaStreamRecorder: onChunk is not set'"
759
+ ]
760
+ });
761
+ this._mediaRecorder.ondataavailable = (event) => this._ondataavailable(event);
762
+ this._mediaRecorder.start(this._config.interval);
763
+ }
764
+ async stop() {
765
+ if (this._mediaRecorder?.state !== "recording") {
766
+ return;
767
+ }
768
+ this._mediaRecorder.stop();
769
+ }
770
+ async _ondataavailable(event) {
771
+ const blob = event.data;
772
+ const uint8Array = new Uint8Array(await blob.arrayBuffer());
773
+ const isHeader = uint8Array[0] === 82 && // R
774
+ uint8Array[1] === 73 && // I
775
+ uint8Array[2] === 70 && // F
776
+ uint8Array[3] === 70;
777
+ let wav;
778
+ if (isHeader) {
779
+ wav = new WaveFile(uint8Array);
780
+ this._header = uint8Array.slice(0, 44);
781
+ } else if (this._header) {
782
+ wav = new WaveFile(new Uint8Array([
783
+ ...this._header,
784
+ ...uint8Array
785
+ ]));
786
+ } else {
787
+ log4.warn("MediaStreamRecorder: no header", void 0, {
788
+ F: __dxlog_file4,
789
+ L: 111,
790
+ S: this,
791
+ C: (f, a) => f(...a)
792
+ });
793
+ return;
794
+ }
795
+ this._onChunk({
796
+ timestamp: Date.now(),
797
+ data: wav.getSamples()
798
+ });
799
+ }
800
+ };
801
+ _ts_decorate([
802
+ synchronized
803
+ ], MediaStreamRecorder.prototype, "_ondataavailable", null);
804
+ MediaStreamRecorder = _ts_decorate([
805
+ trace.resource()
806
+ ], MediaStreamRecorder);
807
+
808
+ // src/transcriber/transcriber.ts
809
+ import { WaveFile as WaveFile2 } from "wavefile";
810
+ import { DeferredTask, Trigger } from "@dxos/async";
811
+ import { LifecycleState, Resource } from "@dxos/context";
812
+ import { log as log5 } from "@dxos/log";
813
+ import { trace as trace2 } from "@dxos/tracing";
814
+ import { TRANSCRIPTION_URL } from "#types";
815
+ function _ts_decorate2(decorators, target, key, desc) {
816
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
817
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
818
+ 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;
819
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
820
+ }
821
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/transcriber/transcriber.ts";
822
+ var Transcriber = class extends Resource {
823
+ _audioChunks = [];
824
+ _lastUsedTimestamp = 0;
825
+ _openTrigger = new Trigger({
826
+ autoReset: true
827
+ });
828
+ _config;
829
+ _recorder;
830
+ _onSegments;
831
+ _recording = false;
832
+ _transcribeTask = void 0;
833
+ constructor({ config, recorder, onSegments }) {
834
+ super();
835
+ this._config = config;
836
+ this._recorder = recorder;
837
+ this._onSegments = onSegments;
838
+ }
839
+ async _open(ctx) {
840
+ log5.info("opening", void 0, {
841
+ F: __dxlog_file5,
842
+ L: 102,
843
+ S: this,
844
+ C: (f, a) => f(...a)
845
+ });
846
+ this._recorder.setOnChunk((chunk) => this._saveAudioChunk(chunk));
847
+ await this._recorder.start();
848
+ this._transcribeTask = new DeferredTask(ctx, async () => this._transcribe());
849
+ this._openTrigger.wake();
850
+ }
851
+ async _close() {
852
+ log5.info("closing", void 0, {
853
+ F: __dxlog_file5,
854
+ L: 110,
855
+ S: this,
856
+ C: (f, a) => f(...a)
857
+ });
858
+ this._recording = false;
859
+ this._transcribeTask = void 0;
860
+ await this._recorder.stop();
861
+ }
862
+ startChunksRecording() {
863
+ log5.info("starting", void 0, {
864
+ F: __dxlog_file5,
865
+ L: 117,
866
+ S: this,
867
+ C: (f, a) => f(...a)
868
+ });
869
+ if (this._lifecycleState !== LifecycleState.OPEN) {
870
+ return;
871
+ }
872
+ this._recording = true;
873
+ }
874
+ stopChunksRecording() {
875
+ if (this._lifecycleState !== LifecycleState.OPEN || !this._recording) {
876
+ return;
877
+ }
878
+ this._recording = false;
879
+ this._transcribeTask?.schedule();
880
+ log5.info("stopped", void 0, {
881
+ F: __dxlog_file5,
882
+ L: 132,
883
+ S: this,
884
+ C: (f, a) => f(...a)
885
+ });
886
+ }
887
+ _saveAudioChunk(chunk) {
888
+ log5("saving audio chunk", {
889
+ chunk
890
+ }, {
891
+ F: __dxlog_file5,
892
+ L: 136,
893
+ S: this,
894
+ C: (f, a) => f(...a)
895
+ });
896
+ this._audioChunks.push(chunk);
897
+ if (!this._recording && (!this._transcribeTask || !this._transcribeTask.scheduled) && this._audioChunks.length >= this._config.prefixBufferChunksAmount) {
898
+ this._audioChunks = this._dropOldChunks();
899
+ } else if (this._audioChunks.length >= this._config.transcribeAfterChunksAmount && this._recording) {
900
+ this._transcribeTask?.schedule();
901
+ }
902
+ }
903
+ async _transcribe() {
904
+ log5("transcribing", {
905
+ chunks: this._audioChunks.length
906
+ }, {
907
+ F: __dxlog_file5,
908
+ L: 152,
909
+ S: this,
910
+ C: (f, a) => f(...a)
911
+ });
912
+ const chunks = this._audioChunks;
913
+ this._audioChunks = this._dropOldChunks();
914
+ if (chunks.length === 0) {
915
+ return;
916
+ }
917
+ const audio = await this._mergeAudioChunks(chunks);
918
+ const segments = await this._fetchTranscription(audio);
919
+ if (!Array.isArray(segments) || segments.length === 0) {
920
+ return;
921
+ }
922
+ const alignedSegments = this._alignSegments(segments, chunks);
923
+ if (alignedSegments.length > 0) {
924
+ await this._onSegments(alignedSegments);
925
+ }
926
+ }
927
+ _dropOldChunks() {
928
+ return this._config.prefixBufferChunksAmount > 0 ? this._audioChunks.slice(-this._config.prefixBufferChunksAmount) : [];
929
+ }
930
+ async _mergeAudioChunks(chunks) {
931
+ const file = new WaveFile2();
932
+ const wavConfig = this._recorder.wavConfig;
933
+ file.fromScratch(wavConfig.channels, wavConfig.sampleRate, wavConfig.bitDepthCode, mergeFloat64Arrays(chunks.map(({ data }) => data)));
934
+ return file.toBase64();
935
+ }
936
+ async _fetchTranscription(audio) {
937
+ if (audio.length === 0) {
938
+ this._audioChunks = [];
939
+ throw new Error("No audio to send for transcribing");
940
+ }
941
+ const response = await fetch(`${TRANSCRIPTION_URL}/transcribe`, {
942
+ method: "POST",
943
+ body: JSON.stringify({
944
+ audio
945
+ }),
946
+ headers: {
947
+ "Content-Type": "application/json"
948
+ }
949
+ });
950
+ if (!response.ok) {
951
+ this._audioChunks = [];
952
+ throw new Error(`Transcription failed: ${response.statusText}`);
953
+ }
954
+ const { segments } = await response.json();
955
+ log5.info("transcription response", {
956
+ segments: segments.length,
957
+ string: segments.map((segments2) => segments2.text).join(" ")
958
+ }, {
959
+ F: __dxlog_file5,
960
+ L: 216,
961
+ S: this,
962
+ C: (f, a) => f(...a)
963
+ });
964
+ return segments;
965
+ }
966
+ _alignSegments(segments, originalChunks) {
967
+ const zeroTimestamp = originalChunks.at(0).timestamp;
968
+ const filteredSegments = segments.filter((segment) => zeroTimestamp + segment.end * 1e3 >= this._lastUsedTimestamp).map((segment) => {
969
+ const words = segment.words.filter((word) => zeroTimestamp + word.start * 1e3 >= this._lastUsedTimestamp);
970
+ return {
971
+ ...segment,
972
+ words,
973
+ text: words.map((word) => word.word).join(""),
974
+ start: words.at(0)?.start ?? segment.end
975
+ };
976
+ }).filter((segment) => segment.words.length > 0);
977
+ if (filteredSegments.length === 0) {
978
+ return [];
979
+ }
980
+ this._lastUsedTimestamp = zeroTimestamp + filteredSegments.at(-1).end * 1e3;
981
+ return filteredSegments.map((segment) => ({
982
+ _tag: "transcript",
983
+ started: new Date(zeroTimestamp + segment.start * 1e3).toISOString(),
984
+ text: segment.text.trim()
985
+ }));
986
+ }
987
+ };
988
+ _ts_decorate2([
989
+ trace2.span({
990
+ showInBrowserTimeline: true
991
+ })
992
+ ], Transcriber.prototype, "_mergeAudioChunks", null);
993
+ _ts_decorate2([
994
+ trace2.span({
995
+ showInBrowserTimeline: true
996
+ })
997
+ ], Transcriber.prototype, "_fetchTranscription", null);
998
+
999
+ // src/transcriber/transcription-manager.ts
1000
+ import { Atom } from "@effect-atom/atom-react";
1001
+ import { synchronized as synchronized2 } from "@dxos/async";
1002
+ import { Resource as Resource2 } from "@dxos/context";
35
1003
  import { Obj } from "@dxos/echo";
36
- import { ClientCapabilities, ClientEvents } from "@dxos/plugin-client";
37
- import { getSpace } from "@dxos/react-client/echo";
38
1004
  import { Message } from "@dxos/types";
1005
+ function _ts_decorate3(decorators, target, key, desc) {
1006
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1007
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1008
+ 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;
1009
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1010
+ }
1011
+ var RECORD_INTERVAL = 200;
1012
+ var PREFIXED_CHUNKS_AMOUNT = 10;
1013
+ var TRANSCRIBE_AFTER_CHUNKS_AMOUNT = 50;
1014
+ var TranscriptionManager = class extends Resource2 {
1015
+ _edgeClient;
1016
+ _messageEnricher;
1017
+ _registry;
1018
+ _audioStreamTrack = void 0;
1019
+ _identityDid = void 0;
1020
+ _mediaRecorder = void 0;
1021
+ _transcriber = void 0;
1022
+ _queue = void 0;
1023
+ _enabledAtom = Atom.make(false);
1024
+ constructor(options) {
1025
+ super();
1026
+ this._edgeClient = options.edgeClient;
1027
+ this._messageEnricher = options.messageEnricher;
1028
+ this._registry = options.registry;
1029
+ }
1030
+ get enabled() {
1031
+ return this._enabledAtom;
1032
+ }
1033
+ getEnabled() {
1034
+ return this._registry.get(this._enabledAtom);
1035
+ }
1036
+ setQueue(queue) {
1037
+ this._queue = queue;
1038
+ return this;
1039
+ }
1040
+ setIdentityDid(did) {
1041
+ if (this._identityDid !== did) {
1042
+ this._identityDid = did;
1043
+ }
1044
+ return this;
1045
+ }
1046
+ setRecording(recording) {
1047
+ if (!this.isOpen || !this.getEnabled()) {
1048
+ return this;
1049
+ }
1050
+ if (recording) {
1051
+ this._transcriber?.startChunksRecording();
1052
+ } else {
1053
+ this._transcriber?.stopChunksRecording();
1054
+ }
1055
+ return this;
1056
+ }
1057
+ async setEnabled(enabled) {
1058
+ if (this.getEnabled() === enabled) {
1059
+ return;
1060
+ }
1061
+ this._registry.set(this._enabledAtom, enabled ?? false);
1062
+ if (enabled) {
1063
+ await this._maybeRestartTranscriber();
1064
+ } else {
1065
+ await this._stopTranscriber();
1066
+ }
1067
+ }
1068
+ async setAudioTrack(track) {
1069
+ if (this._audioStreamTrack === track) {
1070
+ return;
1071
+ }
1072
+ this._audioStreamTrack = track;
1073
+ await this._maybeRestartTranscriber();
1074
+ }
1075
+ async _open() {
1076
+ await this._maybeRestartTranscriber();
1077
+ }
1078
+ async _close() {
1079
+ await this._stopTranscriber();
1080
+ }
1081
+ async _maybeRestartTranscriber() {
1082
+ if (!this._audioStreamTrack || !this.getEnabled() || !this.isOpen) {
1083
+ return;
1084
+ }
1085
+ let needReinit = false;
1086
+ if (this._audioStreamTrack !== this._mediaRecorder?.mediaStreamTrack) {
1087
+ this._mediaRecorder = new MediaStreamRecorder({
1088
+ mediaStreamTrack: this._audioStreamTrack,
1089
+ config: {
1090
+ interval: RECORD_INTERVAL
1091
+ }
1092
+ });
1093
+ needReinit = true;
1094
+ }
1095
+ if (needReinit) {
1096
+ await this._transcriber?.close();
1097
+ this._transcriber = new Transcriber({
1098
+ config: {
1099
+ transcribeAfterChunksAmount: TRANSCRIBE_AFTER_CHUNKS_AMOUNT,
1100
+ prefixBufferChunksAmount: PREFIXED_CHUNKS_AMOUNT
1101
+ },
1102
+ recorder: this._mediaRecorder,
1103
+ onSegments: (segments) => this._onSegments(segments)
1104
+ });
1105
+ await this._transcriber?.open();
1106
+ }
1107
+ }
1108
+ async _stopTranscriber() {
1109
+ await this._transcriber?.close();
1110
+ }
1111
+ async _onSegments(segments) {
1112
+ if (!this.isOpen || !this._queue) {
1113
+ return;
1114
+ }
1115
+ let block = Obj.make(Message.Message, {
1116
+ created: (/* @__PURE__ */ new Date()).toISOString(),
1117
+ blocks: segments,
1118
+ sender: {
1119
+ identityDid: this._identityDid
1120
+ }
1121
+ });
1122
+ if (this._messageEnricher) {
1123
+ block = await this._messageEnricher(block);
1124
+ }
1125
+ await this._queue.append([
1126
+ block
1127
+ ]);
1128
+ }
1129
+ };
1130
+ _ts_decorate3([
1131
+ synchronized2
1132
+ ], TranscriptionManager.prototype, "setAudioTrack", null);
1133
+
1134
+ // src/TranscriptionPlugin.tsx
1135
+ import * as Option from "effect/Option";
1136
+ import { Plugin } from "@dxos/app-framework";
1137
+ import { AppActivationEvents, AppPlugin } from "@dxos/app-toolkit";
1138
+ import { Annotation, Obj as Obj2 } from "@dxos/echo";
1139
+ import { getSpace } from "@dxos/react-client/echo";
1140
+ import { Message as Message2, Transcript } from "@dxos/types";
1141
+ import { BlueprintDefinition, OperationHandler, ReactSurface, Transcriber as Transcriber2 } from "#capabilities";
1142
+ import { meta as meta3 } from "#meta";
39
1143
 
40
1144
  // src/translations.ts
1145
+ import { meta as meta2 } from "#meta";
41
1146
  var translations = [
42
1147
  {
43
1148
  "en-US": {
44
- [meta.id]: {
45
- "plugin name": "Transcription",
46
- "transcript companion label": "Transcript",
47
- "delete button": "Delete",
48
- "bookmark button": "Bookmark",
49
- "scroll to end label": "Scroll to latest",
50
- "settings entity extraction label": "Entity extraction"
1149
+ [meta2.id]: {
1150
+ "plugin.name": "Transcription",
1151
+ "transcript-companion.label": "Transcript",
1152
+ "delete.button": "Delete",
1153
+ "bookmark.button": "Bookmark",
1154
+ "scroll-to-end.label": "Scroll to latest",
1155
+ "settings.entity-extraction.label": "Entity extraction"
51
1156
  }
52
1157
  }
53
1158
  }
54
1159
  ];
55
1160
 
56
1161
  // src/TranscriptionPlugin.tsx
57
- var TranscriptionPlugin = definePlugin(meta, () => [
58
- defineModule({
59
- id: `${meta.id}/module/translations`,
60
- activatesOn: Events.SetupTranslations,
61
- activate: () => contributes(Capabilities.Translations, translations)
62
- }),
63
- defineModule({
64
- id: `${meta.id}/module/metadata`,
65
- activatesOn: Events.SetupMetadata,
66
- activate: () => contributes(Capabilities.Metadata, {
67
- id: Transcript_exports.Transcript.typename,
68
- metadata: {
69
- icon: "ph--subtitles--regular",
70
- iconHue: "sky",
71
- // TODO(wittjosiah): Factor out. Artifact? Separate capability?
72
- getTextContent: async (transcript) => {
73
- const space = getSpace(transcript);
74
- const members = space?.members.get().map((member) => member.identity) ?? [];
75
- const queue = space?.queues.get(transcript.queue.dxn);
76
- await queue?.refresh();
77
- const content = queue?.objects.filter((message) => Obj.instanceOf(Message.Message, message)).flatMap((message, index) => renderByline(members)(message, index)).join("\n\n");
78
- return content;
79
- }
1162
+ var TranscriptionPlugin = Plugin.define(meta3).pipe(AppPlugin.addBlueprintDefinitionModule({
1163
+ activate: BlueprintDefinition
1164
+ }), AppPlugin.addMetadataModule({
1165
+ metadata: {
1166
+ id: Transcript.Transcript.typename,
1167
+ metadata: {
1168
+ icon: Annotation.IconAnnotation.get(Transcript.Transcript).pipe(Option.getOrThrow).icon,
1169
+ iconHue: Annotation.IconAnnotation.get(Transcript.Transcript).pipe(Option.getOrThrow).hue ?? "white",
1170
+ // TODO(wittjosiah): Factor out. Artifact? Separate capability?
1171
+ getTextContent: async (transcript) => {
1172
+ const space = getSpace(transcript);
1173
+ const members = space?.members.get().map((member) => member.identity) ?? [];
1174
+ const queue = space?.queues.get(transcript.queue.dxn);
1175
+ await queue?.refresh();
1176
+ const content = queue?.objects.filter((message) => Obj2.instanceOf(Message2.Message, message)).flatMap((message, index) => renderByline(members)(message, index)).join("\n\n");
1177
+ return content;
80
1178
  }
81
- })
82
- }),
83
- defineModule({
84
- id: `${meta.id}/module/schema`,
85
- activatesOn: ClientEvents.SetupSchema,
86
- activate: () => [
87
- contributes(ClientCapabilities.Schema, [
88
- Transcript_exports.Transcript
89
- ])
90
- ]
91
- }),
92
- defineModule({
93
- id: `${meta.id}/module/react-surface`,
94
- activatesOn: Events.SetupReactSurface,
95
- activate: ReactSurface
96
- }),
97
- defineModule({
98
- id: `${meta.id}/module/intent-resolver`,
99
- activatesOn: Events.SetupIntentResolver,
100
- activate: IntentResolver
101
- }),
102
- defineModule({
103
- id: `${meta.id}/module/transcription`,
104
- activatesOn: Events.SetupAppGraph,
105
- activate: Transcriber2
106
- }),
107
- defineModule({
108
- id: `${meta.id}/module/blueprint`,
109
- activatesOn: Events.SetupArtifactDefinition,
110
- activate: BlueprintDefinition
111
- })
112
- ]);
1179
+ }
1180
+ }
1181
+ }), AppPlugin.addOperationHandlerModule({
1182
+ activate: OperationHandler
1183
+ }), AppPlugin.addSchemaModule({
1184
+ schema: [
1185
+ Transcript.Transcript
1186
+ ]
1187
+ }), AppPlugin.addSurfaceModule({
1188
+ activate: ReactSurface
1189
+ }), AppPlugin.addTranslationsModule({
1190
+ translations
1191
+ }), Plugin.addModule({
1192
+ id: "transcription",
1193
+ activatesOn: AppActivationEvents.SetupAppGraph,
1194
+ activate: Transcriber2
1195
+ }), Plugin.make);
113
1196
  export {
114
1197
  MediaStreamRecorder,
115
1198
  Transcriber,
116
- TranscriptContainer,
117
- TranscriptView,
1199
+ Transcription,
118
1200
  TranscriptionCapabilities,
119
- TranscriptionContainer,
120
1201
  TranscriptionManager,
121
1202
  TranscriptionPlugin,
122
1203
  meta,