@dxos/plugin-transcription 0.8.4-main.ae835ea → 0.8.4-main.bcb3aa67d6

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