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