@dxos/plugin-transcription 0.8.4-main.bc674ce → 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 (221) hide show
  1. package/dist/lib/browser/blueprints/index.mjs +27 -5
  2. package/dist/lib/browser/blueprints/index.mjs.map +4 -4
  3. package/dist/lib/browser/chunk-RIUSFL62.mjs +112 -0
  4. package/dist/lib/browser/chunk-RIUSFL62.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-YXU2ZO6W.mjs +17 -0
  6. package/dist/lib/browser/chunk-YXU2ZO6W.mjs.map +7 -0
  7. package/dist/lib/browser/create-IZPB7VZW.mjs +19 -0
  8. package/dist/lib/browser/create-IZPB7VZW.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +1154 -60
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/meta.json +1 -1
  12. package/dist/lib/browser/open-SN5O5I6X.mjs +29 -0
  13. package/dist/lib/browser/open-SN5O5I6X.mjs.map +7 -0
  14. package/dist/lib/browser/operations/index.mjs +13 -0
  15. package/dist/lib/browser/operations/index.mjs.map +7 -0
  16. package/dist/lib/browser/summarize-5MZENAU5.mjs +71 -0
  17. package/dist/lib/browser/summarize-5MZENAU5.mjs.map +7 -0
  18. package/dist/lib/browser/types/index.mjs +1 -5
  19. package/dist/lib/node-esm/blueprints/index.mjs +27 -5
  20. package/dist/lib/node-esm/blueprints/index.mjs.map +4 -4
  21. package/dist/lib/node-esm/chunk-3GSFEN4L.mjs +113 -0
  22. package/dist/lib/node-esm/chunk-3GSFEN4L.mjs.map +7 -0
  23. package/dist/lib/node-esm/chunk-BR2R6PCT.mjs +19 -0
  24. package/dist/lib/node-esm/chunk-BR2R6PCT.mjs.map +7 -0
  25. package/dist/lib/node-esm/create-TQDGXQBL.mjs +20 -0
  26. package/dist/lib/node-esm/create-TQDGXQBL.mjs.map +7 -0
  27. package/dist/lib/node-esm/index.mjs +1154 -60
  28. package/dist/lib/node-esm/index.mjs.map +4 -4
  29. package/dist/lib/node-esm/meta.json +1 -1
  30. package/dist/lib/node-esm/open-4GAAWWKJ.mjs +30 -0
  31. package/dist/lib/node-esm/open-4GAAWWKJ.mjs.map +7 -0
  32. package/dist/lib/node-esm/operations/index.mjs +14 -0
  33. package/dist/lib/node-esm/operations/index.mjs.map +7 -0
  34. package/dist/lib/node-esm/summarize-RVOSRHEC.mjs +72 -0
  35. package/dist/lib/node-esm/summarize-RVOSRHEC.mjs.map +7 -0
  36. package/dist/lib/node-esm/types/index.mjs +1 -5
  37. package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
  38. package/dist/types/src/blueprints/index.d.ts +1 -1
  39. package/dist/types/src/blueprints/index.d.ts.map +1 -1
  40. package/dist/types/src/blueprints/transcription-blueprint.d.ts +3 -20
  41. package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -1
  42. package/dist/types/src/capabilities/blueprint-definition.d.ts +6 -0
  43. package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
  44. package/dist/types/src/capabilities/index.d.ts +6 -4
  45. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  46. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  47. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/react-surface.d.ts +5 -0
  49. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/transcriber.d.ts +9 -0
  51. package/dist/types/src/capabilities/transcriber.d.ts.map +1 -0
  52. package/dist/types/src/components/{Transcript/Transcript.d.ts → Transcription/Transcription.d.ts} +10 -5
  53. package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -0
  54. package/dist/types/src/components/{Transcript/Transcript.stories.d.ts → Transcription/Transcription.stories.d.ts} +12 -12
  55. package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -0
  56. package/dist/types/src/components/Transcription/index.d.ts +2 -0
  57. package/dist/types/src/components/Transcription/index.d.ts.map +1 -0
  58. package/dist/types/src/components/{Transcript/transcript-extension.d.ts → Transcription/transcription-extension.d.ts} +3 -3
  59. package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -0
  60. package/dist/types/src/components/index.d.ts +1 -3
  61. package/dist/types/src/components/index.d.ts.map +1 -1
  62. package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +1 -1
  63. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +2 -2
  64. package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +1 -1
  65. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +2 -2
  66. package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +1 -1
  67. package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +1 -1
  68. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +6 -0
  69. package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +1 -0
  70. package/dist/types/src/containers/TranscriptionContainer/index.d.ts +3 -0
  71. package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +1 -0
  72. package/dist/types/src/containers/index.d.ts +3 -0
  73. package/dist/types/src/containers/index.d.ts.map +1 -0
  74. package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
  75. package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
  76. package/dist/types/src/operations/create.d.ts +5 -0
  77. package/dist/types/src/operations/create.d.ts.map +1 -0
  78. package/dist/types/src/operations/definitions.d.ts +905 -0
  79. package/dist/types/src/operations/definitions.d.ts.map +1 -0
  80. package/dist/types/src/operations/index.d.ts +4 -0
  81. package/dist/types/src/operations/index.d.ts.map +1 -0
  82. package/dist/types/src/operations/open.d.ts +5 -0
  83. package/dist/types/src/operations/open.d.ts.map +1 -0
  84. package/dist/types/src/operations/summarize.d.ts +8 -0
  85. package/dist/types/src/operations/summarize.d.ts.map +1 -0
  86. package/dist/types/src/segments-normalization/normalization.d.ts +41 -200
  87. package/dist/types/src/segments-normalization/normalization.d.ts.map +1 -1
  88. package/dist/types/src/testing/testing.d.ts.map +1 -1
  89. package/dist/types/src/translations.d.ts +7 -7
  90. package/dist/types/src/translations.d.ts.map +1 -1
  91. package/dist/types/src/types/index.d.ts +0 -2
  92. package/dist/types/src/types/index.d.ts.map +1 -1
  93. package/dist/types/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +86 -65
  95. package/src/TranscriptionPlugin.tsx +17 -13
  96. package/src/blueprints/index.ts +1 -1
  97. package/src/blueprints/transcription-blueprint.ts +13 -8
  98. package/src/capabilities/blueprint-definition.ts +17 -0
  99. package/src/capabilities/index.ts +10 -4
  100. package/src/capabilities/operation-handler.ts +16 -0
  101. package/src/capabilities/react-surface.tsx +30 -0
  102. package/src/capabilities/{transcriber/transcriber.ts → transcriber.ts} +6 -6
  103. package/src/components/{Transcript/Transcript.stories.tsx → Transcription/Transcription.stories.tsx} +17 -14
  104. package/src/components/Transcription/Transcription.tsx +65 -0
  105. package/src/components/{Transcript → Transcription}/index.ts +1 -1
  106. package/src/components/{Transcript/transcript-extension.ts → Transcription/transcription-extension.ts} +4 -4
  107. package/src/components/index.ts +1 -6
  108. package/src/components/stories/FileTranscription.stories.tsx +9 -11
  109. package/src/components/stories/KeyWordRecognition.stories.tsx +6 -6
  110. package/src/components/stories/MicrophoneTranscription.stories.tsx +13 -31
  111. package/src/components/stories/TranscriptionStory.tsx +7 -5
  112. package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +32 -0
  113. package/src/containers/TranscriptionContainer/index.ts +7 -0
  114. package/src/containers/index.ts +7 -0
  115. package/src/hooks/useTranscriber.ts +2 -2
  116. package/src/meta.ts +1 -1
  117. package/src/model/model.test.ts +6 -6
  118. package/src/operations/create.ts +22 -0
  119. package/src/operations/definitions.ts +102 -0
  120. package/src/operations/index.ts +13 -0
  121. package/src/operations/open.ts +31 -0
  122. package/src/{functions → operations}/summarize.ts +41 -47
  123. package/src/segments-normalization/normalization.test.ts +1 -1
  124. package/src/segments-normalization/normalization.ts +33 -71
  125. package/src/testing/testing.ts +1 -11
  126. package/src/transcriber/transcriber.ts +1 -1
  127. package/src/translations.ts +7 -7
  128. package/src/types/capabilities.ts +3 -3
  129. package/src/types/index.ts +0 -2
  130. package/dist/lib/browser/TranscriptContainer-6EHKWVS4.mjs +0 -12
  131. package/dist/lib/browser/TranscriptContainer-6EHKWVS4.mjs.map +0 -7
  132. package/dist/lib/browser/blueprint-definition-FTRNMLJZ.mjs +0 -18
  133. package/dist/lib/browser/blueprint-definition-FTRNMLJZ.mjs.map +0 -7
  134. package/dist/lib/browser/chunk-ICM7JRCO.mjs +0 -698
  135. package/dist/lib/browser/chunk-ICM7JRCO.mjs.map +0 -7
  136. package/dist/lib/browser/chunk-Q5OC3WVG.mjs +0 -72
  137. package/dist/lib/browser/chunk-Q5OC3WVG.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-SYGEFDWO.mjs +0 -8
  139. package/dist/lib/browser/chunk-SYGEFDWO.mjs.map +0 -7
  140. package/dist/lib/browser/chunk-TKFU53OZ.mjs +0 -460
  141. package/dist/lib/browser/chunk-TKFU53OZ.mjs.map +0 -7
  142. package/dist/lib/browser/chunk-ZZF7ICVM.mjs +0 -150
  143. package/dist/lib/browser/chunk-ZZF7ICVM.mjs.map +0 -7
  144. package/dist/lib/browser/operation-resolver-ZUWTU7C5.mjs +0 -22
  145. package/dist/lib/browser/operation-resolver-ZUWTU7C5.mjs.map +0 -7
  146. package/dist/lib/browser/react-surface-ZXH5267I.mjs +0 -34
  147. package/dist/lib/browser/react-surface-ZXH5267I.mjs.map +0 -7
  148. package/dist/lib/browser/transcriber-EZZTUCPE.mjs +0 -60
  149. package/dist/lib/browser/transcriber-EZZTUCPE.mjs.map +0 -7
  150. package/dist/lib/node-esm/TranscriptContainer-5YT5W73I.mjs +0 -13
  151. package/dist/lib/node-esm/TranscriptContainer-5YT5W73I.mjs.map +0 -7
  152. package/dist/lib/node-esm/blueprint-definition-IOX35YUX.mjs +0 -19
  153. package/dist/lib/node-esm/blueprint-definition-IOX35YUX.mjs.map +0 -7
  154. package/dist/lib/node-esm/chunk-4BFY6P2D.mjs +0 -73
  155. package/dist/lib/node-esm/chunk-4BFY6P2D.mjs.map +0 -7
  156. package/dist/lib/node-esm/chunk-BX2AB7S7.mjs +0 -699
  157. package/dist/lib/node-esm/chunk-BX2AB7S7.mjs.map +0 -7
  158. package/dist/lib/node-esm/chunk-CWP22IHV.mjs +0 -151
  159. package/dist/lib/node-esm/chunk-CWP22IHV.mjs.map +0 -7
  160. package/dist/lib/node-esm/chunk-EPC27WWV.mjs +0 -461
  161. package/dist/lib/node-esm/chunk-EPC27WWV.mjs.map +0 -7
  162. package/dist/lib/node-esm/chunk-GVAVEVZ6.mjs +0 -10
  163. package/dist/lib/node-esm/chunk-GVAVEVZ6.mjs.map +0 -7
  164. package/dist/lib/node-esm/operation-resolver-Z72B4H2Q.mjs +0 -23
  165. package/dist/lib/node-esm/operation-resolver-Z72B4H2Q.mjs.map +0 -7
  166. package/dist/lib/node-esm/react-surface-6GUBZX2V.mjs +0 -35
  167. package/dist/lib/node-esm/react-surface-6GUBZX2V.mjs.map +0 -7
  168. package/dist/lib/node-esm/transcriber-RGW3YYT4.mjs +0 -61
  169. package/dist/lib/node-esm/transcriber-RGW3YYT4.mjs.map +0 -7
  170. package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts +0 -9
  171. package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts.map +0 -1
  172. package/dist/types/src/capabilities/blueprint-definition/index.d.ts +0 -3
  173. package/dist/types/src/capabilities/blueprint-definition/index.d.ts.map +0 -1
  174. package/dist/types/src/capabilities/operation-handler/index.d.ts +0 -3
  175. package/dist/types/src/capabilities/operation-handler/index.d.ts.map +0 -1
  176. package/dist/types/src/capabilities/operation-handler/operation-handler.d.ts +0 -5
  177. package/dist/types/src/capabilities/operation-handler/operation-handler.d.ts.map +0 -1
  178. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  179. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  180. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  181. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  182. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  183. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  184. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +0 -5
  185. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  186. package/dist/types/src/capabilities/transcriber/index.d.ts +0 -3
  187. package/dist/types/src/capabilities/transcriber/index.d.ts.map +0 -1
  188. package/dist/types/src/capabilities/transcriber/transcriber.d.ts +0 -9
  189. package/dist/types/src/capabilities/transcriber/transcriber.d.ts.map +0 -1
  190. package/dist/types/src/components/Transcript/Transcript.d.ts.map +0 -1
  191. package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +0 -1
  192. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +0 -28
  193. package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +0 -1
  194. package/dist/types/src/components/Transcript/index.d.ts +0 -2
  195. package/dist/types/src/components/Transcript/index.d.ts.map +0 -1
  196. package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +0 -1
  197. package/dist/types/src/components/TranscriptContainer.d.ts +0 -7
  198. package/dist/types/src/components/TranscriptContainer.d.ts.map +0 -1
  199. package/dist/types/src/functions/index.d.ts +0 -3
  200. package/dist/types/src/functions/index.d.ts.map +0 -1
  201. package/dist/types/src/functions/open.d.ts +0 -7
  202. package/dist/types/src/functions/open.d.ts.map +0 -1
  203. package/dist/types/src/functions/summarize.d.ts +0 -11
  204. package/dist/types/src/functions/summarize.d.ts.map +0 -1
  205. package/dist/types/src/types/TranscriptAction.d.ts +0 -25
  206. package/dist/types/src/types/TranscriptAction.d.ts.map +0 -1
  207. package/src/capabilities/blueprint-definition/blueprint-definition.ts +0 -23
  208. package/src/capabilities/blueprint-definition/index.ts +0 -7
  209. package/src/capabilities/operation-handler/index.ts +0 -7
  210. package/src/capabilities/operation-handler/operation-handler.ts +0 -25
  211. package/src/capabilities/operation-resolver/index.ts +0 -7
  212. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -25
  213. package/src/capabilities/react-surface/index.ts +0 -7
  214. package/src/capabilities/react-surface/react-surface.tsx +0 -27
  215. package/src/capabilities/transcriber/index.ts +0 -7
  216. package/src/components/Transcript/Transcript.tsx +0 -66
  217. package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
  218. package/src/components/TranscriptContainer.tsx +0 -34
  219. package/src/functions/index.ts +0 -6
  220. package/src/functions/open.ts +0 -39
  221. package/src/types/TranscriptAction.ts +0 -32
@@ -1,110 +1,1204 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
  import {
3
- TranscriptContainer
4
- } from "./chunk-GVAVEVZ6.mjs";
5
- import {
6
- TranscriptView,
7
- TranscriptionContainer,
8
- useAudioFile,
9
- useAudioTrack,
10
- useQueueModelAdapter,
11
- useTranscriber,
12
- useVoiceInput
13
- } from "./chunk-BX2AB7S7.mjs";
14
- import {
15
- MediaStreamRecorder,
16
- Transcriber,
17
- TranscriptionManager
18
- } from "./chunk-EPC27WWV.mjs";
3
+ TranscriptionCapabilities
4
+ } from "./chunk-BR2R6PCT.mjs";
19
5
  import {
6
+ mergeFloat64Arrays,
20
7
  renderByline
21
8
  } from "./chunk-NVM2QUMX.mjs";
22
- import {
23
- TranscriptionCapabilities,
24
- meta
25
- } from "./chunk-4BFY6P2D.mjs";
26
9
  import "./chunk-HSLMI22Q.mjs";
27
10
 
28
- // src/TranscriptionPlugin.tsx
29
- import { Common, Plugin } from "@dxos/app-framework";
30
- import { Obj } from "@dxos/echo";
31
- import { getSpace } from "@dxos/react-client/echo";
32
- import { Message, Transcript } from "@dxos/types";
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
+ };
33
606
 
34
- // src/capabilities/blueprint-definition/index.ts
35
- import { Capability } from "@dxos/app-framework";
36
- var BlueprintDefinition = Capability.lazy("BlueprintDefinition", () => import("./blueprint-definition-IOX35YUX.mjs"));
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
+ };
37
668
 
38
- // src/capabilities/operation-resolver/index.ts
39
- import { Capability as Capability2 } from "@dxos/app-framework";
40
- var OperationResolver = Capability2.lazy("OperationResolver", () => import("./operation-resolver-Z72B4H2Q.mjs"));
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
+ };
41
686
 
42
- // src/capabilities/react-surface/index.ts
43
- import { Capability as Capability3 } from "@dxos/app-framework";
44
- var ReactSurface = Capability3.lazy("ReactSurface", () => import("./react-surface-6GUBZX2V.mjs"));
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);
45
808
 
46
- // src/capabilities/transcriber/index.ts
47
- import { Capability as Capability4 } from "@dxos/app-framework";
48
- var Transcriber2 = Capability4.lazy("Transcriber", () => import("./transcriber-RGW3YYT4.mjs"));
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";
1004
+ import { Obj } from "@dxos/echo";
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";
1140
+ import { getSpace } from "@dxos/react-client/echo";
1141
+ import { Message as Message2, Transcript } from "@dxos/types";
1142
+ import { meta as meta3 } from "#meta";
49
1143
 
50
1144
  // src/translations.ts
1145
+ import { meta as meta2 } from "#meta";
51
1146
  var translations = [
52
1147
  {
53
1148
  "en-US": {
54
- [meta.id]: {
55
- "plugin name": "Transcription",
56
- "transcript companion label": "Transcript",
57
- "delete button": "Delete",
58
- "bookmark button": "Bookmark",
59
- "scroll to end label": "Scroll to latest",
60
- "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"
61
1156
  }
62
1157
  }
63
1158
  }
64
1159
  ];
65
1160
 
66
1161
  // src/TranscriptionPlugin.tsx
67
- var TranscriptionPlugin = Plugin.define(meta).pipe(Common.Plugin.addTranslationsModule({
68
- translations
69
- }), Common.Plugin.addMetadataModule({
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({
70
1166
  metadata: {
71
1167
  id: Transcript.Transcript.typename,
72
1168
  metadata: {
73
- icon: "ph--subtitles--regular",
74
- iconHue: "sky",
1169
+ icon: Annotation.IconAnnotation.get(Transcript.Transcript).pipe(Option.getOrThrow).icon,
1170
+ iconHue: Annotation.IconAnnotation.get(Transcript.Transcript).pipe(Option.getOrThrow).hue ?? "white",
75
1171
  // TODO(wittjosiah): Factor out. Artifact? Separate capability?
76
1172
  getTextContent: async (transcript) => {
77
1173
  const space = getSpace(transcript);
78
1174
  const members = space?.members.get().map((member) => member.identity) ?? [];
79
1175
  const queue = space?.queues.get(transcript.queue.dxn);
80
1176
  await queue?.refresh();
81
- const content = queue?.objects.filter((message) => Obj.instanceOf(Message.Message, message)).flatMap((message, index) => renderByline(members)(message, index)).join("\n\n");
1177
+ const content = queue?.objects.filter((message) => Obj2.instanceOf(Message2.Message, message)).flatMap((message, index) => renderByline(members)(message, index)).join("\n\n");
82
1178
  return content;
83
1179
  }
84
1180
  }
85
1181
  }
86
- }), Common.Plugin.addSchemaModule({
1182
+ }), AppPlugin.addOperationHandlerModule({
1183
+ activate: OperationHandler
1184
+ }), AppPlugin.addSchemaModule({
87
1185
  schema: [
88
1186
  Transcript.Transcript
89
1187
  ]
90
- }), Common.Plugin.addSurfaceModule({
1188
+ }), AppPlugin.addSurfaceModule({
91
1189
  activate: ReactSurface
92
- }), Common.Plugin.addOperationResolverModule({
93
- activate: OperationResolver
1190
+ }), AppPlugin.addTranslationsModule({
1191
+ translations
94
1192
  }), Plugin.addModule({
95
1193
  id: "transcription",
96
- activatesOn: Common.ActivationEvent.SetupAppGraph,
1194
+ activatesOn: AppActivationEvents.SetupAppGraph,
97
1195
  activate: Transcriber2
98
- }), Common.Plugin.addBlueprintDefinitionModule({
99
- activate: BlueprintDefinition
100
1196
  }), Plugin.make);
101
1197
  export {
102
1198
  MediaStreamRecorder,
103
1199
  Transcriber,
104
- TranscriptContainer,
105
- TranscriptView,
1200
+ Transcription,
106
1201
  TranscriptionCapabilities,
107
- TranscriptionContainer,
108
1202
  TranscriptionManager,
109
1203
  TranscriptionPlugin,
110
1204
  meta,