@dxos/plugin-transcription 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef
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.
- package/dist/lib/browser/blueprints/index.mjs +1 -1
- package/dist/lib/browser/blueprints/index.mjs.map +3 -3
- package/dist/lib/browser/{chunk-5LWSJZTZ.mjs → chunk-3L623EVL.mjs} +2 -2
- package/dist/lib/browser/chunk-3L623EVL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-HOOPZ7EM.mjs → chunk-KAEXRX3Y.mjs} +1 -1
- package/dist/lib/browser/{chunk-RIUSFL62.mjs → chunk-L4HW35L6.mjs} +4 -5
- package/dist/lib/browser/chunk-L4HW35L6.mjs.map +7 -0
- package/dist/lib/browser/chunk-MLRJWAQH.mjs +179 -0
- package/dist/lib/browser/chunk-MLRJWAQH.mjs.map +7 -0
- package/dist/lib/browser/chunk-NQQK7UZQ.mjs +244 -0
- package/dist/lib/browser/chunk-NQQK7UZQ.mjs.map +7 -0
- package/dist/lib/browser/components/index.mjs +207 -0
- package/dist/lib/browser/components/index.mjs.map +7 -0
- package/dist/lib/browser/{create-IZPB7VZW.mjs → create-ULR5DPJZ.mjs} +6 -4
- package/dist/lib/browser/create-ULR5DPJZ.mjs.map +7 -0
- package/dist/lib/browser/hooks/index.mjs +17 -0
- package/dist/lib/browser/hooks/index.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +93 -831
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/open-SFMPLZVW.mjs +27 -0
- package/dist/lib/browser/open-SFMPLZVW.mjs.map +7 -0
- package/dist/lib/browser/operations/index.mjs +3 -3
- package/dist/lib/browser/operations/index.mjs.map +3 -3
- package/dist/lib/browser/{summarize-5MZENAU5.mjs → summarize-BDFX4APN.mjs} +15 -8
- package/dist/lib/browser/summarize-BDFX4APN.mjs.map +7 -0
- package/dist/lib/browser/types/index.mjs +1 -1
- package/dist/lib/node-esm/blueprints/index.mjs +1 -1
- package/dist/lib/node-esm/blueprints/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-3GSFEN4L.mjs → chunk-7UIZWCD4.mjs} +4 -5
- package/dist/lib/node-esm/chunk-7UIZWCD4.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs +181 -0
- package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-X74WYCCE.mjs → chunk-KO3IOD2O.mjs} +2 -2
- package/dist/lib/node-esm/chunk-KO3IOD2O.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-MQK4R6GT.mjs +245 -0
- package/dist/lib/node-esm/chunk-MQK4R6GT.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-NVM2QUMX.mjs → chunk-O533NBMK.mjs} +1 -1
- package/dist/lib/node-esm/components/index.mjs +208 -0
- package/dist/lib/node-esm/components/index.mjs.map +7 -0
- package/dist/lib/node-esm/{create-TQDGXQBL.mjs → create-A5QHNWRF.mjs} +6 -4
- package/dist/lib/node-esm/create-A5QHNWRF.mjs.map +7 -0
- package/dist/lib/node-esm/hooks/index.mjs +18 -0
- package/dist/lib/node-esm/hooks/index.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +93 -831
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/open-OI24QNTO.mjs +28 -0
- package/dist/lib/node-esm/open-OI24QNTO.mjs.map +7 -0
- package/dist/lib/node-esm/operations/index.mjs +3 -3
- package/dist/lib/node-esm/operations/index.mjs.map +3 -3
- package/dist/lib/node-esm/{summarize-RVOSRHEC.mjs → summarize-3UNN24I2.mjs} +15 -8
- package/dist/lib/node-esm/summarize-3UNN24I2.mjs.map +7 -0
- package/dist/lib/node-esm/types/index.mjs +1 -1
- package/dist/types/src/TranscriptionPlugin.d.ts +1 -0
- package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
- package/dist/types/src/TranscriptionPlugin.node.d.ts +4 -0
- package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -0
- package/dist/types/src/TranscriptionPlugin.test.d.ts +2 -0
- package/dist/types/src/TranscriptionPlugin.test.d.ts.map +1 -0
- package/dist/types/src/blueprints/transcription-blueprint.d.ts +2 -2
- package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -1
- package/dist/types/src/capabilities/blueprint-definition.d.ts +2 -2
- package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +2 -2
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
- package/dist/types/src/components/Transcription/Transcription.d.ts +1 -1
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts +7 -7
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -1
- package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -1
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +1 -1
- package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
- package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
- package/dist/types/src/hooks/useTranscriber.d.ts +1 -1
- package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
- package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/model.d.ts +2 -2
- package/dist/types/src/model/model.d.ts.map +1 -1
- package/dist/types/src/{segments-normalization → normalization}/index.d.ts +1 -1
- package/dist/types/src/normalization/index.d.ts.map +1 -0
- package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -0
- package/dist/types/src/{segments-normalization → normalization}/normalization.d.ts +155 -153
- package/dist/types/src/normalization/normalization.d.ts.map +1 -0
- package/dist/types/src/normalization/normalization.test.d.ts.map +1 -0
- package/dist/types/src/normalization/utils.d.ts.map +1 -0
- package/dist/types/src/operations/create.d.ts +1 -1
- package/dist/types/src/operations/create.d.ts.map +1 -1
- package/dist/types/src/operations/definitions.d.ts +394 -382
- package/dist/types/src/operations/definitions.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts +1 -1
- package/dist/types/src/operations/index.d.ts.map +1 -1
- package/dist/types/src/operations/open.d.ts +1 -1
- package/dist/types/src/operations/open.d.ts.map +1 -1
- package/dist/types/src/operations/summarize.d.ts +1 -1
- package/dist/types/src/operations/summarize.d.ts.map +1 -1
- package/dist/types/src/{components/stories → stories}/FileTranscription.stories.d.ts +3 -3
- package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -0
- package/dist/types/src/{components/stories → stories}/MicrophoneTranscription.stories.d.ts +1 -1
- package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts +23 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -0
- package/dist/types/src/stories/useIsSpeaking.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/transcriber/index.d.ts +1 -1
- package/dist/types/src/transcriber/index.d.ts.map +1 -1
- package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcriber.d.ts +57 -2
- package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
- package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +7 -8
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/capabilities.d.ts +2 -1
- package/dist/types/src/types/capabilities.d.ts.map +1 -1
- package/dist/types/src/types/{types.d.ts → defs.d.ts} +1 -1
- package/dist/types/src/types/defs.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +1 -1
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/array.d.ts.map +1 -1
- package/dist/types/src/util/get-time-str.d.ts.map +1 -1
- package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
- package/dist/types/src/util/render-byline.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +76 -67
- package/src/TranscriptionPlugin.node.ts +48 -0
- package/src/TranscriptionPlugin.test.ts +31 -0
- package/src/TranscriptionPlugin.tsx +7 -3
- package/src/blueprints/transcription-blueprint.ts +2 -3
- package/src/capabilities/blueprint-definition.ts +2 -0
- package/src/capabilities/index.ts +2 -1
- package/src/capabilities/operation-handler.ts +1 -1
- package/src/capabilities/react-surface.tsx +4 -2
- package/src/capabilities/transcriber.ts +2 -0
- package/src/components/Transcription/Transcription.stories.tsx +3 -5
- package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +4 -2
- package/src/hooks/useTranscriber.ts +3 -1
- package/src/index.ts +9 -4
- package/src/model/model.test.ts +3 -3
- package/src/model/model.ts +3 -3
- package/src/{segments-normalization → normalization}/message-normalizer.ts +1 -5
- package/src/{segments-normalization → normalization}/normalization.ts +5 -5
- package/src/operations/create.ts +4 -2
- package/src/operations/definitions.ts +3 -4
- package/src/operations/index.ts +1 -1
- package/src/operations/open.ts +5 -8
- package/src/operations/summarize.ts +11 -7
- package/src/{components/stories → stories}/FileTranscription.stories.tsx +24 -5
- package/src/{components/stories → stories}/KeyWordRecognition.stories.tsx +30 -28
- package/src/{components/stories → stories}/MicrophoneTranscription.stories.tsx +3 -3
- package/src/stories/TranscriptionStory.tsx +93 -0
- package/src/transcriber/index.ts +1 -1
- package/src/transcriber/transcriber.test.ts +104 -0
- package/src/transcriber/transcriber.ts +92 -51
- package/src/types/capabilities.ts +6 -1
- package/src/types/index.ts +1 -1
- package/dist/lib/browser/chunk-5LWSJZTZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-RIUSFL62.mjs.map +0 -7
- package/dist/lib/browser/create-IZPB7VZW.mjs.map +0 -7
- package/dist/lib/browser/open-SN5O5I6X.mjs +0 -29
- package/dist/lib/browser/open-SN5O5I6X.mjs.map +0 -7
- package/dist/lib/browser/summarize-5MZENAU5.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-3GSFEN4L.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-X74WYCCE.mjs.map +0 -7
- package/dist/lib/node-esm/create-TQDGXQBL.mjs.map +0 -7
- package/dist/lib/node-esm/open-4GAAWWKJ.mjs +0 -30
- package/dist/lib/node-esm/open-4GAAWWKJ.mjs.map +0 -7
- package/dist/lib/node-esm/summarize-RVOSRHEC.mjs.map +0 -7
- package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/TranscriptionStory.d.ts +0 -12
- package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +0 -1
- package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/index.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/normalization.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/normalization.test.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/utils.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/src/components/stories/TranscriptionStory.tsx +0 -43
- package/dist/lib/browser/{chunk-HOOPZ7EM.mjs.map → chunk-KAEXRX3Y.mjs.map} +0 -0
- package/dist/lib/node-esm/{chunk-NVM2QUMX.mjs.map → chunk-O533NBMK.mjs.map} +0 -0
- package/dist/types/src/{segments-normalization → normalization}/message-normalizer.d.ts +0 -0
- package/dist/types/src/{segments-normalization → normalization}/normalization.test.d.ts +0 -0
- package/dist/types/src/{segments-normalization → normalization}/utils.d.ts +0 -0
- package/dist/types/src/{components/stories → stories}/KeyWordRecognition.stories.d.ts +0 -0
- package/dist/types/src/{components/stories → stories}/useIsSpeaking.d.ts +0 -0
- package/src/{segments-normalization → normalization}/index.ts +1 -1
- /package/src/{segments-normalization → normalization}/normalization.test.ts +0 -0
- /package/src/{segments-normalization → normalization}/utils.ts +0 -0
- /package/src/{components/stories → stories}/useIsSpeaking.tsx +0 -0
- /package/src/types/{types.ts → defs.ts} +0 -0
|
@@ -1,669 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useAudioFile,
|
|
3
|
+
useAudioTrack,
|
|
4
|
+
useQueueModelAdapter,
|
|
5
|
+
useTranscriber,
|
|
6
|
+
useVoiceInput
|
|
7
|
+
} from "./chunk-NQQK7UZQ.mjs";
|
|
8
|
+
import "./chunk-MLRJWAQH.mjs";
|
|
1
9
|
import {
|
|
2
10
|
TranscriptionCapabilities
|
|
3
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3L623EVL.mjs";
|
|
4
12
|
import {
|
|
5
|
-
mergeFloat64Arrays
|
|
6
|
-
|
|
7
|
-
} from "./chunk-HOOPZ7EM.mjs";
|
|
13
|
+
mergeFloat64Arrays
|
|
14
|
+
} from "./chunk-KAEXRX3Y.mjs";
|
|
8
15
|
import "./chunk-J5LGTIGS.mjs";
|
|
9
16
|
|
|
10
|
-
// src/
|
|
11
|
-
import
|
|
12
|
-
import { useThemeContext } from "@dxos/react-ui";
|
|
13
|
-
import { useTextEditor } from "@dxos/react-ui-editor";
|
|
14
|
-
import { autoScroll, createBasicExtensions, createMarkdownExtensions, createThemeExtensions, decorateMarkdown, documentSlots, preview } from "@dxos/ui-editor";
|
|
15
|
-
import { composable, composableProps } from "@dxos/ui-theme";
|
|
16
|
-
import { isTruthy } from "@dxos/util";
|
|
17
|
-
|
|
18
|
-
// src/components/Transcription/transcription-extension.ts
|
|
19
|
-
import { RangeSetBuilder } from "@codemirror/state";
|
|
20
|
-
import { EditorView, GutterMarker, ViewPlugin, gutter } from "@codemirror/view";
|
|
21
|
-
import { format } from "date-fns/format";
|
|
22
|
-
import { intervalToDuration } from "date-fns/intervalToDuration";
|
|
23
|
-
import { addEventListener, combine } from "@dxos/async";
|
|
24
|
-
|
|
25
|
-
// src/model/model.ts
|
|
26
|
-
import { Text } from "@codemirror/state";
|
|
27
|
-
import { Event } from "@dxos/async";
|
|
28
|
-
import { invariant } from "@dxos/invariant";
|
|
29
|
-
import { log } from "@dxos/log";
|
|
30
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/model/model.ts";
|
|
31
|
-
var DocumentAdapter = class {
|
|
32
|
-
_view;
|
|
33
|
-
constructor(_view) {
|
|
34
|
-
this._view = _view;
|
|
35
|
-
}
|
|
36
|
-
/** The document must always have at least one line. */
|
|
37
|
-
lineCount() {
|
|
38
|
-
return this._view.state.doc.lines - 1;
|
|
39
|
-
}
|
|
40
|
-
replaceLines(from, remove, insert) {
|
|
41
|
-
log("replaceLines", {
|
|
42
|
-
count: this._view.state.doc.lines,
|
|
43
|
-
from,
|
|
44
|
-
remove,
|
|
45
|
-
insert
|
|
46
|
-
}, {
|
|
47
|
-
F: __dxlog_file,
|
|
48
|
-
L: 39,
|
|
49
|
-
S: this,
|
|
50
|
-
C: (f, a) => f(...a)
|
|
51
|
-
});
|
|
52
|
-
const numLines = this._view.state.doc.lines;
|
|
53
|
-
const posFrom = this._view.state.doc.line(from).from;
|
|
54
|
-
const posTo = remove === 0 ? posFrom : this._view.state.doc.line(Math.min(numLines, from + remove)).from;
|
|
55
|
-
let text = insert.join("\n");
|
|
56
|
-
if (posTo === this._view.state.doc.length) {
|
|
57
|
-
text += "\n";
|
|
58
|
-
}
|
|
59
|
-
this._view.dispatch({
|
|
60
|
-
changes: {
|
|
61
|
-
from: posFrom,
|
|
62
|
-
to: posTo,
|
|
63
|
-
insert: text
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
var SerializationModel = class {
|
|
69
|
-
_renderer;
|
|
70
|
-
/** Ordered set of chunks. */
|
|
71
|
-
_chunks = [];
|
|
72
|
-
/** Map of chunk IDs to their current line counts. */
|
|
73
|
-
_chunkLineCounts = /* @__PURE__ */ new Map();
|
|
74
|
-
/** Map of line numbers to chunk IDs. */
|
|
75
|
-
_lineToChunk = /* @__PURE__ */ new Map();
|
|
76
|
-
/** Track chunk changes since last sync. */
|
|
77
|
-
_changes = [];
|
|
78
|
-
/** Emits when the document is updated. */
|
|
79
|
-
update = new Event();
|
|
80
|
-
constructor(_renderer, initialChunks = []) {
|
|
81
|
-
this._renderer = _renderer;
|
|
82
|
-
initialChunks.forEach((chunk) => {
|
|
83
|
-
this.appendChunk(chunk);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
toJSON() {
|
|
87
|
-
return {
|
|
88
|
-
chunks: this._chunks.length,
|
|
89
|
-
changes: this._changes.length
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
get doc() {
|
|
93
|
-
return Text.of([
|
|
94
|
-
...this._chunks.flatMap((chunk, index) => this._renderer(chunk, index)),
|
|
95
|
-
""
|
|
96
|
-
]);
|
|
97
|
-
}
|
|
98
|
-
get chunks() {
|
|
99
|
-
return this._chunks;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Get the block ID at the given line number.
|
|
103
|
-
*/
|
|
104
|
-
getChunkAtLine(line) {
|
|
105
|
-
return this._lineToChunk.get(line);
|
|
106
|
-
}
|
|
107
|
-
reset() {
|
|
108
|
-
this._chunks.length = 0;
|
|
109
|
-
this._chunkLineCounts.clear();
|
|
110
|
-
this._lineToChunk.clear();
|
|
111
|
-
this.update.emit();
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
appendChunk(chunk) {
|
|
115
|
-
const index = this._chunks.length;
|
|
116
|
-
this._chunks.push(chunk);
|
|
117
|
-
this._changes.push({
|
|
118
|
-
type: "append",
|
|
119
|
-
index,
|
|
120
|
-
chunk
|
|
121
|
-
});
|
|
122
|
-
this.update.emit();
|
|
123
|
-
return this;
|
|
124
|
-
}
|
|
125
|
-
updateChunk(chunk) {
|
|
126
|
-
const index = this._chunks.findIndex((c) => c.id === chunk.id);
|
|
127
|
-
invariant(index !== -1, void 0, {
|
|
128
|
-
F: __dxlog_file,
|
|
129
|
-
L: 147,
|
|
130
|
-
S: this,
|
|
131
|
-
A: [
|
|
132
|
-
"index !== -1",
|
|
133
|
-
""
|
|
134
|
-
]
|
|
135
|
-
});
|
|
136
|
-
this._chunks[index] = chunk;
|
|
137
|
-
this._changes.push({
|
|
138
|
-
type: "update",
|
|
139
|
-
index,
|
|
140
|
-
chunk
|
|
141
|
-
});
|
|
142
|
-
this.update.emit();
|
|
143
|
-
return this;
|
|
144
|
-
}
|
|
145
|
-
deleteBlock(id) {
|
|
146
|
-
const index = this._chunks.findIndex((c) => c.id === id);
|
|
147
|
-
invariant(index !== -1, void 0, {
|
|
148
|
-
F: __dxlog_file,
|
|
149
|
-
L: 156,
|
|
150
|
-
S: this,
|
|
151
|
-
A: [
|
|
152
|
-
"index !== -1",
|
|
153
|
-
""
|
|
154
|
-
]
|
|
155
|
-
});
|
|
156
|
-
this._chunks.splice(index, 1);
|
|
157
|
-
this._changes.push({
|
|
158
|
-
type: "delete",
|
|
159
|
-
index,
|
|
160
|
-
id
|
|
161
|
-
});
|
|
162
|
-
this.update.emit();
|
|
163
|
-
return this;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Syncs the tracked changes with the ChunkDocument.
|
|
167
|
-
*/
|
|
168
|
-
sync(document2) {
|
|
169
|
-
log("sync", {
|
|
170
|
-
changes: this._changes
|
|
171
|
-
}, {
|
|
172
|
-
F: __dxlog_file,
|
|
173
|
-
L: 167,
|
|
174
|
-
S: this,
|
|
175
|
-
C: (f, a) => f(...a)
|
|
176
|
-
});
|
|
177
|
-
for (const change of this._changes) {
|
|
178
|
-
switch (change.type) {
|
|
179
|
-
case "append": {
|
|
180
|
-
const lines = this._renderer(change.chunk, change.index);
|
|
181
|
-
const lastLine = document2.lineCount();
|
|
182
|
-
document2.replaceLines(lastLine + 1, 0, lines);
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
case "update": {
|
|
186
|
-
let lineStart = 1;
|
|
187
|
-
for (let i = 0; i < change.index; i++) {
|
|
188
|
-
lineStart += this._chunkLineCounts.get(this._chunks[i].id);
|
|
189
|
-
}
|
|
190
|
-
const previousLineCount = this._chunkLineCounts.get(change.chunk.id);
|
|
191
|
-
const newLines = this._renderer(change.chunk, change.index);
|
|
192
|
-
document2.replaceLines(lineStart, previousLineCount, newLines);
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
case "delete": {
|
|
196
|
-
let lineStart = 1;
|
|
197
|
-
for (let i = 0; i < change.index; i++) {
|
|
198
|
-
lineStart += this._chunkLineCounts.get(this._chunks[i].id);
|
|
199
|
-
}
|
|
200
|
-
const previousLineCount = this._chunkLineCounts.get(change.id);
|
|
201
|
-
document2.replaceLines(lineStart, previousLineCount, []);
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
this._lineToChunk.clear();
|
|
207
|
-
let currentLine = 1;
|
|
208
|
-
for (const [index, chunk] of this._chunks.entries()) {
|
|
209
|
-
const lineCount = this._renderer(chunk, index).length;
|
|
210
|
-
this._chunkLineCounts.set(chunk.id, lineCount);
|
|
211
|
-
this._lineToChunk.set(currentLine, chunk);
|
|
212
|
-
currentLine += lineCount;
|
|
213
|
-
}
|
|
214
|
-
const chunkIds = new Set(this._chunks.map((chunk) => chunk.id));
|
|
215
|
-
for (const id of this._chunkLineCounts.keys()) {
|
|
216
|
-
if (!chunkIds.has(id)) {
|
|
217
|
-
this._chunkLineCounts.delete(id);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
this._changes.length = 0;
|
|
221
|
-
return this;
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
// src/components/Transcription/transcription-extension.ts
|
|
226
|
-
var transcription = ({ model, started }) => {
|
|
227
|
-
return [
|
|
228
|
-
// Show timestamps in the gutter.
|
|
229
|
-
gutter({
|
|
230
|
-
class: "cm-timestamp-gutter",
|
|
231
|
-
lineMarkerChange: (update) => update.docChanged || update.viewportChanged,
|
|
232
|
-
markers: (view) => {
|
|
233
|
-
const start = getStartTime(started, model.chunks[0]);
|
|
234
|
-
const builder = new RangeSetBuilder();
|
|
235
|
-
for (const { from, to } of view.visibleRanges) {
|
|
236
|
-
let line = view.state.doc.lineAt(from);
|
|
237
|
-
while (line.from <= to) {
|
|
238
|
-
const block = model.getChunkAtLine(line.number)?.blocks[0];
|
|
239
|
-
const timestamp = block?._tag === "transcript" && block.started;
|
|
240
|
-
if (timestamp) {
|
|
241
|
-
builder.add(line.from, line.from, new TimestampMarker(line.number, new Date(timestamp), start));
|
|
242
|
-
}
|
|
243
|
-
if (line.to + 1 > view.state.doc.length) {
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
line = view.state.doc.lineAt(line.to + 1);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return builder.finish();
|
|
250
|
-
}
|
|
251
|
-
}),
|
|
252
|
-
// Listen for model updates.
|
|
253
|
-
ViewPlugin.fromClass(class {
|
|
254
|
-
_controls;
|
|
255
|
-
_cleanup;
|
|
256
|
-
_adapter;
|
|
257
|
-
_initialized = false;
|
|
258
|
-
constructor(view) {
|
|
259
|
-
this._adapter = new DocumentAdapter(view);
|
|
260
|
-
const scroller = view.scrollDOM;
|
|
261
|
-
let isAutoScrolling = false;
|
|
262
|
-
let hasScrolled = false;
|
|
263
|
-
let timeout;
|
|
264
|
-
const scrollToBottom = (smooth = false) => {
|
|
265
|
-
scroller.style.scrollBehavior = smooth ? "smooth" : "";
|
|
266
|
-
scroller.classList.add("cm-hide-scrollbar");
|
|
267
|
-
isAutoScrolling = true;
|
|
268
|
-
clearTimeout(timeout);
|
|
269
|
-
timeout = setTimeout(() => {
|
|
270
|
-
this._controls?.classList.add("opacity-0");
|
|
271
|
-
scroller.classList.remove("cm-hide-scrollbar");
|
|
272
|
-
isAutoScrolling = false;
|
|
273
|
-
}, 500);
|
|
274
|
-
view.dispatch({
|
|
275
|
-
effects: EditorView.scrollIntoView(view.state.doc.length, {
|
|
276
|
-
y: "end"
|
|
277
|
-
})
|
|
278
|
-
});
|
|
279
|
-
};
|
|
280
|
-
this._cleanup = combine(addEventListener(view.scrollDOM, "scroll", () => {
|
|
281
|
-
if (!isAutoScrolling) {
|
|
282
|
-
hasScrolled = true;
|
|
283
|
-
this._controls?.classList.remove("opacity-0");
|
|
284
|
-
}
|
|
285
|
-
}), model.update.on(() => {
|
|
286
|
-
const autoScroll2 = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight === 0 || !hasScrolled;
|
|
287
|
-
model.sync(this._adapter);
|
|
288
|
-
if (autoScroll2) {
|
|
289
|
-
scrollToBottom(true);
|
|
290
|
-
}
|
|
291
|
-
}));
|
|
292
|
-
}
|
|
293
|
-
update(_update) {
|
|
294
|
-
if (!this._initialized) {
|
|
295
|
-
this._initialized = true;
|
|
296
|
-
setTimeout(() => {
|
|
297
|
-
model.sync(this._adapter);
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
destroy() {
|
|
302
|
-
this._controls?.remove();
|
|
303
|
-
this._cleanup();
|
|
304
|
-
}
|
|
305
|
-
}),
|
|
306
|
-
EditorView.theme({
|
|
307
|
-
".cm-gutters": {
|
|
308
|
-
backgroundColor: "var(--color-base-surface)"
|
|
309
|
-
},
|
|
310
|
-
".cm-timestamp-gutter": {
|
|
311
|
-
width: "6rem",
|
|
312
|
-
paddingLeft: "0.5rem",
|
|
313
|
-
paddingRight: "1rem"
|
|
314
|
-
},
|
|
315
|
-
".cm-timestamp-gutter > .cm-gutterElement > div": {
|
|
316
|
-
display: "inline-flex",
|
|
317
|
-
textAlign: "right",
|
|
318
|
-
padding: "3px"
|
|
319
|
-
}
|
|
320
|
-
})
|
|
321
|
-
];
|
|
322
|
-
};
|
|
323
|
-
var TimestampMarker = class extends GutterMarker {
|
|
324
|
-
_line;
|
|
325
|
-
_timestamp;
|
|
326
|
-
_started;
|
|
327
|
-
constructor(_line, _timestamp, _started) {
|
|
328
|
-
super(), this._line = _line, this._timestamp = _timestamp, this._started = _started;
|
|
329
|
-
}
|
|
330
|
-
eq(other) {
|
|
331
|
-
return this._timestamp === other._timestamp;
|
|
332
|
-
}
|
|
333
|
-
toDOM(view) {
|
|
334
|
-
const el = document.createElement("div");
|
|
335
|
-
el.className = "text-sm text-subdued hover:bg-hover-surface cursor-pointer";
|
|
336
|
-
el.textContent = formatTimestamp(this._timestamp, this._started);
|
|
337
|
-
el.onclick = () => {
|
|
338
|
-
const pos = view.state.doc.line(this._line).from;
|
|
339
|
-
view.dispatch({
|
|
340
|
-
effects: EditorView.scrollIntoView(pos, {
|
|
341
|
-
y: "start"
|
|
342
|
-
})
|
|
343
|
-
});
|
|
344
|
-
};
|
|
345
|
-
return el;
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
var getStartTime = (started, message) => {
|
|
349
|
-
if (started) {
|
|
350
|
-
return started;
|
|
351
|
-
}
|
|
352
|
-
if (message?.blocks[0]?._tag === "transcript" && message.blocks[0].started) {
|
|
353
|
-
return new Date(message.blocks[0].started);
|
|
354
|
-
}
|
|
355
|
-
return void 0;
|
|
356
|
-
};
|
|
357
|
-
var formatTimestamp = (timestamp, relative) => {
|
|
358
|
-
if (relative) {
|
|
359
|
-
const pad = (n = 0) => String(n).padStart(2, "0");
|
|
360
|
-
const { hours, minutes, seconds } = intervalToDuration({
|
|
361
|
-
start: relative,
|
|
362
|
-
end: timestamp
|
|
363
|
-
});
|
|
364
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
|
365
|
-
} else {
|
|
366
|
-
return format(timestamp, "HH:mm:ss");
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
// src/components/Transcription/Transcription.tsx
|
|
371
|
-
var Transcription = composable(({ transcript: object, model, children, ...props }, forwardedRef) => {
|
|
372
|
-
const { themeMode } = useThemeContext();
|
|
373
|
-
const { parentRef } = useTextEditor(() => {
|
|
374
|
-
return {
|
|
375
|
-
extensions: [
|
|
376
|
-
createBasicExtensions({
|
|
377
|
-
readOnly: true,
|
|
378
|
-
lineWrapping: true,
|
|
379
|
-
search: true
|
|
380
|
-
}),
|
|
381
|
-
createMarkdownExtensions(),
|
|
382
|
-
createThemeExtensions({
|
|
383
|
-
themeMode,
|
|
384
|
-
slots: documentSlots
|
|
385
|
-
}),
|
|
386
|
-
decorateMarkdown(),
|
|
387
|
-
preview(),
|
|
388
|
-
transcription({
|
|
389
|
-
model,
|
|
390
|
-
started: object?.started ? new Date(object.started) : void 0
|
|
391
|
-
}),
|
|
392
|
-
autoScroll()
|
|
393
|
-
].filter(isTruthy)
|
|
394
|
-
};
|
|
395
|
-
}, [
|
|
396
|
-
model
|
|
397
|
-
]);
|
|
398
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
399
|
-
...composableProps(props, {
|
|
400
|
-
classNames: "dx-container"
|
|
401
|
-
}),
|
|
402
|
-
ref: parentRef,
|
|
403
|
-
"data-popover-collision-boundary": true
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
// src/hooks/useAudioFile.ts
|
|
408
|
-
import { useEffect, useState } from "react";
|
|
409
|
-
import { scheduleTask } from "@dxos/async";
|
|
410
|
-
import { Context } from "@dxos/context";
|
|
411
|
-
import { log as log2 } from "@dxos/log";
|
|
412
|
-
var __dxlog_file2 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/hooks/useAudioFile.ts";
|
|
413
|
-
var useAudioFile = (audioUrl, constraints) => {
|
|
414
|
-
const [{ audio, stream, track }, setStream] = useState({});
|
|
415
|
-
useEffect(() => {
|
|
416
|
-
const ctx = new Context(void 0, {
|
|
417
|
-
F: __dxlog_file2,
|
|
418
|
-
L: 20
|
|
419
|
-
});
|
|
420
|
-
scheduleTask(ctx, async () => {
|
|
421
|
-
try {
|
|
422
|
-
const response = await fetch(audioUrl);
|
|
423
|
-
const blob = await response.blob();
|
|
424
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
425
|
-
ctx.onDispose(() => {
|
|
426
|
-
URL.revokeObjectURL(objectUrl);
|
|
427
|
-
});
|
|
428
|
-
const audio2 = new Audio();
|
|
429
|
-
audio2.src = objectUrl;
|
|
430
|
-
await new Promise((resolve, reject) => {
|
|
431
|
-
audio2.addEventListener("error", (err) => {
|
|
432
|
-
log2.error("error", {
|
|
433
|
-
err
|
|
434
|
-
}, {
|
|
435
|
-
F: __dxlog_file2,
|
|
436
|
-
L: 39,
|
|
437
|
-
S: void 0,
|
|
438
|
-
C: (f, a) => f(...a)
|
|
439
|
-
});
|
|
440
|
-
reject(err);
|
|
441
|
-
}, {
|
|
442
|
-
once: true
|
|
443
|
-
});
|
|
444
|
-
audio2.addEventListener("canplay", async () => {
|
|
445
|
-
resolve();
|
|
446
|
-
}, {
|
|
447
|
-
once: true
|
|
448
|
-
});
|
|
449
|
-
audio2.load();
|
|
450
|
-
});
|
|
451
|
-
const audioCtx = new AudioContext();
|
|
452
|
-
if (audioCtx.state === "suspended") {
|
|
453
|
-
await audioCtx.resume();
|
|
454
|
-
}
|
|
455
|
-
const destination = audioCtx.createMediaStreamDestination();
|
|
456
|
-
destination.channelCount = 1;
|
|
457
|
-
const source = audioCtx.createMediaElementSource(audio2);
|
|
458
|
-
source.connect(destination);
|
|
459
|
-
const track2 = destination.stream.getAudioTracks()[0];
|
|
460
|
-
if (constraints) {
|
|
461
|
-
await track2.applyConstraints(constraints).catch((err) => log2.catch(err, void 0, {
|
|
462
|
-
F: __dxlog_file2,
|
|
463
|
-
L: 68,
|
|
464
|
-
S: void 0,
|
|
465
|
-
C: (f, a) => f(...a)
|
|
466
|
-
}));
|
|
467
|
-
}
|
|
468
|
-
setStream({
|
|
469
|
-
audio: audio2,
|
|
470
|
-
stream: destination.stream,
|
|
471
|
-
track: destination.stream.getAudioTracks()[0]
|
|
472
|
-
});
|
|
473
|
-
} catch (err) {
|
|
474
|
-
log2.catch(err, void 0, {
|
|
475
|
-
F: __dxlog_file2,
|
|
476
|
-
L: 78,
|
|
477
|
-
S: void 0,
|
|
478
|
-
C: (f, a) => f(...a)
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
return () => {
|
|
483
|
-
void ctx.dispose();
|
|
484
|
-
};
|
|
485
|
-
}, [
|
|
486
|
-
audioUrl
|
|
487
|
-
]);
|
|
488
|
-
return {
|
|
489
|
-
audio,
|
|
490
|
-
stream,
|
|
491
|
-
track
|
|
492
|
-
};
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
// src/hooks/useAudioTrack.ts
|
|
496
|
-
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
497
|
-
var useAudioTrack = (active, constraints) => {
|
|
498
|
-
const [track, setTrack] = useState2();
|
|
499
|
-
useEffect2(() => {
|
|
500
|
-
if (!active) {
|
|
501
|
-
track?.stop();
|
|
502
|
-
setTrack(void 0);
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const initAudio = async () => {
|
|
506
|
-
const audio = new Audio();
|
|
507
|
-
audio.srcObject = await navigator.mediaDevices.getUserMedia({
|
|
508
|
-
audio: constraints ?? true
|
|
509
|
-
});
|
|
510
|
-
const [track2] = audio.srcObject.getAudioTracks();
|
|
511
|
-
if (track2) {
|
|
512
|
-
setTrack(track2);
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
if (active) {
|
|
516
|
-
void initAudio();
|
|
517
|
-
}
|
|
518
|
-
}, [
|
|
519
|
-
active
|
|
520
|
-
]);
|
|
521
|
-
return track;
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
// src/hooks/useQueueModelAdapter.ts
|
|
525
|
-
import { useEffect as useEffect3, useMemo, useState as useState3 } from "react";
|
|
526
|
-
var useQueueModelAdapter = (renderer, queue, initialChunks = []) => {
|
|
527
|
-
const model = useMemo(() => new SerializationModel(renderer, initialChunks), [
|
|
528
|
-
queue
|
|
529
|
-
]);
|
|
530
|
-
const [loaded, setLoaded] = useState3(false);
|
|
531
|
-
useEffect3(() => {
|
|
532
|
-
if (!queue) {
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
const update = () => {
|
|
536
|
-
for (const block of queue?.objects ?? []) {
|
|
537
|
-
model.appendChunk(block);
|
|
538
|
-
}
|
|
539
|
-
setLoaded(true);
|
|
540
|
-
};
|
|
541
|
-
let i;
|
|
542
|
-
if (queue.isLoading) {
|
|
543
|
-
i = setInterval(() => {
|
|
544
|
-
if (!queue?.isLoading) {
|
|
545
|
-
clearInterval(i);
|
|
546
|
-
update();
|
|
547
|
-
}
|
|
548
|
-
}, 1e3);
|
|
549
|
-
} else {
|
|
550
|
-
update();
|
|
551
|
-
}
|
|
552
|
-
return () => clearTimeout(i);
|
|
553
|
-
}, [
|
|
554
|
-
model,
|
|
555
|
-
queue
|
|
556
|
-
]);
|
|
557
|
-
useEffect3(() => {
|
|
558
|
-
if (!loaded || !queue || model.chunks.length === queue.objects.length) {
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
const chunk = queue.objects.at(-1);
|
|
562
|
-
if (chunk) {
|
|
563
|
-
model.appendChunk(chunk);
|
|
564
|
-
}
|
|
565
|
-
}, [
|
|
566
|
-
model,
|
|
567
|
-
loaded,
|
|
568
|
-
queue?.objects.length
|
|
569
|
-
]);
|
|
570
|
-
return model;
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
// src/hooks/useTranscriber.ts
|
|
574
|
-
import { useEffect as useEffect4, useMemo as useMemo2 } from "react";
|
|
575
|
-
import { useCapabilities } from "@dxos/app-framework/ui";
|
|
576
|
-
import { TranscriptionCapabilities as TranscriptionCapabilities2 } from "#types";
|
|
577
|
-
var useTranscriber = ({ audioStreamTrack, recorderConfig, transcriberConfig, onSegments }) => {
|
|
578
|
-
const [getTranscriber] = useCapabilities(TranscriptionCapabilities2.Transcriber);
|
|
579
|
-
const transcriber = useMemo2(() => {
|
|
580
|
-
if (!getTranscriber || !audioStreamTrack || !onSegments) {
|
|
581
|
-
return void 0;
|
|
582
|
-
}
|
|
583
|
-
return getTranscriber({
|
|
584
|
-
audioStreamTrack,
|
|
585
|
-
recorderConfig,
|
|
586
|
-
transcriberConfig,
|
|
587
|
-
onSegments
|
|
588
|
-
});
|
|
589
|
-
}, [
|
|
590
|
-
getTranscriber,
|
|
591
|
-
audioStreamTrack,
|
|
592
|
-
recorderConfig,
|
|
593
|
-
transcriberConfig,
|
|
594
|
-
onSegments
|
|
595
|
-
]);
|
|
596
|
-
useEffect4(() => {
|
|
597
|
-
return () => {
|
|
598
|
-
void transcriber?.close();
|
|
599
|
-
};
|
|
600
|
-
}, [
|
|
601
|
-
transcriber
|
|
602
|
-
]);
|
|
603
|
-
return transcriber;
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
// src/hooks/useVoiceInput.ts
|
|
607
|
-
import { useCallback, useEffect as useEffect5, useState as useState4 } from "react";
|
|
608
|
-
import { scheduleMicroTask } from "@dxos/async";
|
|
609
|
-
import { Context as Context2 } from "@dxos/context";
|
|
610
|
-
import { log as log3 } from "@dxos/log";
|
|
611
|
-
import { useSoundEffect } from "@dxos/react-ui-sfx";
|
|
612
|
-
var __dxlog_file3 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/hooks/useVoiceInput.ts";
|
|
613
|
-
var useVoiceInput = ({ active, onUpdate }) => {
|
|
614
|
-
const soundStart = useSoundEffect("StartRecording");
|
|
615
|
-
const soundStop = useSoundEffect("StopRecording");
|
|
616
|
-
const handleSegments = useCallback(async (segments) => {
|
|
617
|
-
const text = segments.map((str) => str.text.trim().replace(/[^\w\s]/g, "")).join(" ");
|
|
618
|
-
onUpdate(text);
|
|
619
|
-
}, []);
|
|
620
|
-
const [recording, setRecording] = useState4(false);
|
|
621
|
-
const track = useAudioTrack(active);
|
|
622
|
-
const transcriber = useTranscriber({
|
|
623
|
-
audioStreamTrack: track,
|
|
624
|
-
onSegments: handleSegments
|
|
625
|
-
});
|
|
626
|
-
useEffect5(() => {
|
|
627
|
-
const ctx = new Context2(void 0, {
|
|
628
|
-
F: __dxlog_file3,
|
|
629
|
-
L: 43
|
|
630
|
-
});
|
|
631
|
-
scheduleMicroTask(ctx, async () => {
|
|
632
|
-
if (active && transcriber) {
|
|
633
|
-
await transcriber.open();
|
|
634
|
-
log3.info("starting...", void 0, {
|
|
635
|
-
F: __dxlog_file3,
|
|
636
|
-
L: 47,
|
|
637
|
-
S: void 0,
|
|
638
|
-
C: (f, a) => f(...a)
|
|
639
|
-
});
|
|
640
|
-
setRecording(true);
|
|
641
|
-
void soundStart.play();
|
|
642
|
-
transcriber.startChunksRecording();
|
|
643
|
-
} else if (!active && transcriber?.isOpen) {
|
|
644
|
-
transcriber?.stopChunksRecording();
|
|
645
|
-
await transcriber?.close();
|
|
646
|
-
log3.info("stopped", void 0, {
|
|
647
|
-
F: __dxlog_file3,
|
|
648
|
-
L: 54,
|
|
649
|
-
S: void 0,
|
|
650
|
-
C: (f, a) => f(...a)
|
|
651
|
-
});
|
|
652
|
-
setRecording(false);
|
|
653
|
-
void soundStop.play();
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
return () => {
|
|
657
|
-
void ctx.dispose();
|
|
658
|
-
};
|
|
659
|
-
}, [
|
|
660
|
-
active,
|
|
661
|
-
transcriber
|
|
662
|
-
]);
|
|
663
|
-
return {
|
|
664
|
-
recording
|
|
665
|
-
};
|
|
666
|
-
};
|
|
17
|
+
// src/index.ts
|
|
18
|
+
import { Plugin } from "@dxos/app-framework";
|
|
667
19
|
|
|
668
20
|
// src/meta.ts
|
|
669
21
|
import { trim } from "@dxos/util";
|
|
@@ -687,16 +39,16 @@ var meta = {
|
|
|
687
39
|
import { MediaRecorder, register } from "extendable-media-recorder";
|
|
688
40
|
import { WaveFile } from "wavefile";
|
|
689
41
|
import { synchronized } from "@dxos/async";
|
|
690
|
-
import { invariant
|
|
691
|
-
import { log
|
|
42
|
+
import { invariant } from "@dxos/invariant";
|
|
43
|
+
import { log } from "@dxos/log";
|
|
692
44
|
import { trace } from "@dxos/tracing";
|
|
45
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/transcriber/media-stream-recorder.ts";
|
|
693
46
|
function _ts_decorate(decorators, target, key, desc) {
|
|
694
47
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
695
48
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
696
49
|
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
50
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
698
51
|
}
|
|
699
|
-
var __dxlog_file4 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/transcriber/media-stream-recorder.ts";
|
|
700
52
|
var initializingPromise;
|
|
701
53
|
var initializeExtendableMediaRecorder = async () => {
|
|
702
54
|
const { connect } = await import("extendable-media-recorder-wav-encoder");
|
|
@@ -730,14 +82,9 @@ var MediaStreamRecorder = class {
|
|
|
730
82
|
this._onChunk = onChunk;
|
|
731
83
|
}
|
|
732
84
|
async start() {
|
|
733
|
-
await (initializingPromise ??= initializeExtendableMediaRecorder()).catch((err) =>
|
|
85
|
+
await (initializingPromise ??= initializeExtendableMediaRecorder()).catch((err) => log.info("initializeExtendableMediaRecorder", {
|
|
734
86
|
err
|
|
735
|
-
}, {
|
|
736
|
-
F: __dxlog_file4,
|
|
737
|
-
L: 69,
|
|
738
|
-
S: this,
|
|
739
|
-
C: (f, a) => f(...a)
|
|
740
|
-
}));
|
|
87
|
+
}, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 48, S: this }));
|
|
741
88
|
if (!this._mediaRecorder) {
|
|
742
89
|
const stream = new MediaStream([
|
|
743
90
|
this._mediaStreamTrack
|
|
@@ -749,15 +96,7 @@ var MediaStreamRecorder = class {
|
|
|
749
96
|
if (this._mediaRecorder.state === "recording") {
|
|
750
97
|
return;
|
|
751
98
|
}
|
|
752
|
-
|
|
753
|
-
F: __dxlog_file4,
|
|
754
|
-
L: 80,
|
|
755
|
-
S: this,
|
|
756
|
-
A: [
|
|
757
|
-
"this._onChunk",
|
|
758
|
-
"'MediaStreamRecorder: onChunk is not set'"
|
|
759
|
-
]
|
|
760
|
-
});
|
|
99
|
+
invariant(this._onChunk, "MediaStreamRecorder: onChunk is not set", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 62, S: this, A: ["this._onChunk", "'MediaStreamRecorder: onChunk is not set'"] });
|
|
761
100
|
this._mediaRecorder.ondataavailable = (event) => this._ondataavailable(event);
|
|
762
101
|
this._mediaRecorder.start(this._config.interval);
|
|
763
102
|
}
|
|
@@ -784,12 +123,7 @@ var MediaStreamRecorder = class {
|
|
|
784
123
|
...uint8Array
|
|
785
124
|
]));
|
|
786
125
|
} else {
|
|
787
|
-
|
|
788
|
-
F: __dxlog_file4,
|
|
789
|
-
L: 111,
|
|
790
|
-
S: this,
|
|
791
|
-
C: (f, a) => f(...a)
|
|
792
|
-
});
|
|
126
|
+
log.warn("MediaStreamRecorder: no header", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 90, S: this });
|
|
793
127
|
return;
|
|
794
128
|
}
|
|
795
129
|
this._onChunk({
|
|
@@ -809,16 +143,16 @@ MediaStreamRecorder = _ts_decorate([
|
|
|
809
143
|
import { WaveFile as WaveFile2 } from "wavefile";
|
|
810
144
|
import { DeferredTask, Trigger } from "@dxos/async";
|
|
811
145
|
import { LifecycleState, Resource } from "@dxos/context";
|
|
812
|
-
import { log as
|
|
146
|
+
import { log as log2 } from "@dxos/log";
|
|
813
147
|
import { trace as trace2 } from "@dxos/tracing";
|
|
814
148
|
import { TRANSCRIPTION_URL } from "#types";
|
|
149
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/transcriber/transcriber.ts";
|
|
815
150
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
816
151
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
817
152
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
818
153
|
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
154
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
820
155
|
}
|
|
821
|
-
var __dxlog_file5 = "/__w/dxos/dxos/packages/plugins/plugin-transcription/src/transcriber/transcriber.ts";
|
|
822
156
|
var Transcriber = class extends Resource {
|
|
823
157
|
_audioChunks = [];
|
|
824
158
|
_lastUsedTimestamp = 0;
|
|
@@ -828,44 +162,31 @@ var Transcriber = class extends Resource {
|
|
|
828
162
|
_config;
|
|
829
163
|
_recorder;
|
|
830
164
|
_onSegments;
|
|
165
|
+
_transcribeFn;
|
|
831
166
|
_recording = false;
|
|
832
167
|
_transcribeTask = void 0;
|
|
833
|
-
constructor({ config, recorder, onSegments }) {
|
|
168
|
+
constructor({ config, recorder, onSegments, transcribe }) {
|
|
834
169
|
super();
|
|
835
170
|
this._config = config;
|
|
836
171
|
this._recorder = recorder;
|
|
837
172
|
this._onSegments = onSegments;
|
|
173
|
+
this._transcribeFn = transcribe;
|
|
838
174
|
}
|
|
839
175
|
async _open(ctx) {
|
|
840
|
-
|
|
841
|
-
F: __dxlog_file5,
|
|
842
|
-
L: 102,
|
|
843
|
-
S: this,
|
|
844
|
-
C: (f, a) => f(...a)
|
|
845
|
-
});
|
|
176
|
+
log2.info("opening", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 41, S: this });
|
|
846
177
|
this._recorder.setOnChunk((chunk) => this._saveAudioChunk(chunk));
|
|
847
178
|
await this._recorder.start();
|
|
848
179
|
this._transcribeTask = new DeferredTask(ctx, async () => this._transcribe());
|
|
849
180
|
this._openTrigger.wake();
|
|
850
181
|
}
|
|
851
182
|
async _close() {
|
|
852
|
-
|
|
853
|
-
F: __dxlog_file5,
|
|
854
|
-
L: 110,
|
|
855
|
-
S: this,
|
|
856
|
-
C: (f, a) => f(...a)
|
|
857
|
-
});
|
|
183
|
+
log2.info("closing", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 48, S: this });
|
|
858
184
|
this._recording = false;
|
|
859
185
|
this._transcribeTask = void 0;
|
|
860
186
|
await this._recorder.stop();
|
|
861
187
|
}
|
|
862
188
|
startChunksRecording() {
|
|
863
|
-
|
|
864
|
-
F: __dxlog_file5,
|
|
865
|
-
L: 117,
|
|
866
|
-
S: this,
|
|
867
|
-
C: (f, a) => f(...a)
|
|
868
|
-
});
|
|
189
|
+
log2.info("starting", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 54, S: this });
|
|
869
190
|
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
870
191
|
return;
|
|
871
192
|
}
|
|
@@ -877,22 +198,12 @@ var Transcriber = class extends Resource {
|
|
|
877
198
|
}
|
|
878
199
|
this._recording = false;
|
|
879
200
|
this._transcribeTask?.schedule();
|
|
880
|
-
|
|
881
|
-
F: __dxlog_file5,
|
|
882
|
-
L: 132,
|
|
883
|
-
S: this,
|
|
884
|
-
C: (f, a) => f(...a)
|
|
885
|
-
});
|
|
201
|
+
log2.info("stopped", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 66, S: this });
|
|
886
202
|
}
|
|
887
203
|
_saveAudioChunk(chunk) {
|
|
888
|
-
|
|
204
|
+
log2("saving audio chunk", {
|
|
889
205
|
chunk
|
|
890
|
-
}, {
|
|
891
|
-
F: __dxlog_file5,
|
|
892
|
-
L: 136,
|
|
893
|
-
S: this,
|
|
894
|
-
C: (f, a) => f(...a)
|
|
895
|
-
});
|
|
206
|
+
}, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 69, S: this });
|
|
896
207
|
this._audioChunks.push(chunk);
|
|
897
208
|
if (!this._recording && (!this._transcribeTask || !this._transcribeTask.scheduled) && this._audioChunks.length >= this._config.prefixBufferChunksAmount) {
|
|
898
209
|
this._audioChunks = this._dropOldChunks();
|
|
@@ -901,14 +212,9 @@ var Transcriber = class extends Resource {
|
|
|
901
212
|
}
|
|
902
213
|
}
|
|
903
214
|
async _transcribe() {
|
|
904
|
-
|
|
215
|
+
log2("transcribing", {
|
|
905
216
|
chunks: this._audioChunks.length
|
|
906
|
-
}, {
|
|
907
|
-
F: __dxlog_file5,
|
|
908
|
-
L: 152,
|
|
909
|
-
S: this,
|
|
910
|
-
C: (f, a) => f(...a)
|
|
911
|
-
});
|
|
217
|
+
}, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 81, S: this });
|
|
912
218
|
const chunks = this._audioChunks;
|
|
913
219
|
this._audioChunks = this._dropOldChunks();
|
|
914
220
|
if (chunks.length === 0) {
|
|
@@ -935,54 +241,41 @@ var Transcriber = class extends Resource {
|
|
|
935
241
|
}
|
|
936
242
|
async _fetchTranscription(audio) {
|
|
937
243
|
if (audio.length === 0) {
|
|
938
|
-
this._audioChunks = [];
|
|
939
244
|
throw new Error("No audio to send for transcribing");
|
|
940
245
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
246
|
+
let segments;
|
|
247
|
+
if (this._transcribeFn) {
|
|
248
|
+
segments = await this._transcribeFn(audio);
|
|
249
|
+
} else {
|
|
250
|
+
const endpoint = this._config.endpoint ?? TRANSCRIPTION_URL;
|
|
251
|
+
const response = await fetch(`${endpoint}/transcribe`, {
|
|
252
|
+
method: "POST",
|
|
253
|
+
body: JSON.stringify({
|
|
254
|
+
audio
|
|
255
|
+
}),
|
|
256
|
+
headers: {
|
|
257
|
+
"Content-Type": "application/json"
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
throw new Error(`Transcription failed: ${response.statusText}`);
|
|
948
262
|
}
|
|
949
|
-
|
|
950
|
-
if (!response.ok) {
|
|
951
|
-
this._audioChunks = [];
|
|
952
|
-
throw new Error(`Transcription failed: ${response.statusText}`);
|
|
263
|
+
({ segments } = await response.json());
|
|
953
264
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
L: 216,
|
|
961
|
-
S: this,
|
|
962
|
-
C: (f, a) => f(...a)
|
|
963
|
-
});
|
|
265
|
+
if (!Array.isArray(segments)) {
|
|
266
|
+
throw new Error("Transcription response payload is invalid");
|
|
267
|
+
}
|
|
268
|
+
log2.info("transcription response", {
|
|
269
|
+
segments: segments.length
|
|
270
|
+
}, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 134, S: this });
|
|
964
271
|
return segments;
|
|
965
272
|
}
|
|
966
273
|
_alignSegments(segments, originalChunks) {
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
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 [];
|
|
274
|
+
const result = alignWhisperSegments(segments, originalChunks, this._lastUsedTimestamp);
|
|
275
|
+
if (result.lastUsedTimestamp !== void 0) {
|
|
276
|
+
this._lastUsedTimestamp = result.lastUsedTimestamp;
|
|
979
277
|
}
|
|
980
|
-
|
|
981
|
-
return filteredSegments.map((segment) => ({
|
|
982
|
-
_tag: "transcript",
|
|
983
|
-
started: new Date(zeroTimestamp + segment.start * 1e3).toISOString(),
|
|
984
|
-
text: segment.text.trim()
|
|
985
|
-
}));
|
|
278
|
+
return result.transcripts;
|
|
986
279
|
}
|
|
987
280
|
};
|
|
988
281
|
_ts_decorate2([
|
|
@@ -995,6 +288,36 @@ _ts_decorate2([
|
|
|
995
288
|
showInBrowserTimeline: true
|
|
996
289
|
})
|
|
997
290
|
], Transcriber.prototype, "_fetchTranscription", null);
|
|
291
|
+
var alignWhisperSegments = (segments, originalChunks, lastUsedTimestamp) => {
|
|
292
|
+
if (originalChunks.length === 0) {
|
|
293
|
+
return {
|
|
294
|
+
transcripts: []
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const zeroTimestamp = originalChunks[0].timestamp;
|
|
298
|
+
const filteredSegments = segments.filter((segment) => zeroTimestamp + segment.end * 1e3 >= lastUsedTimestamp).map((segment) => {
|
|
299
|
+
const words = segment.words.filter((word) => zeroTimestamp + word.start * 1e3 >= lastUsedTimestamp);
|
|
300
|
+
return {
|
|
301
|
+
...segment,
|
|
302
|
+
words,
|
|
303
|
+
text: words.map((word) => word.word).join(""),
|
|
304
|
+
start: words.at(0)?.start ?? segment.end
|
|
305
|
+
};
|
|
306
|
+
}).filter((segment) => segment.words.length > 0);
|
|
307
|
+
if (filteredSegments.length === 0) {
|
|
308
|
+
return {
|
|
309
|
+
transcripts: []
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
transcripts: filteredSegments.map((segment) => ({
|
|
314
|
+
_tag: "transcript",
|
|
315
|
+
started: new Date(zeroTimestamp + segment.start * 1e3).toISOString(),
|
|
316
|
+
text: segment.text.trim()
|
|
317
|
+
})),
|
|
318
|
+
lastUsedTimestamp: zeroTimestamp + filteredSegments.at(-1).end * 1e3
|
|
319
|
+
};
|
|
320
|
+
};
|
|
998
321
|
|
|
999
322
|
// src/transcriber/transcription-manager.ts
|
|
1000
323
|
import { Atom } from "@effect-atom/atom-react";
|
|
@@ -1131,77 +454,16 @@ _ts_decorate3([
|
|
|
1131
454
|
synchronized2
|
|
1132
455
|
], TranscriptionManager.prototype, "setAudioTrack", null);
|
|
1133
456
|
|
|
1134
|
-
// src/
|
|
1135
|
-
|
|
1136
|
-
import { Plugin } from "@dxos/app-framework";
|
|
1137
|
-
import { AppActivationEvents, AppPlugin } from "@dxos/app-toolkit";
|
|
1138
|
-
import { Annotation, Obj as Obj2 } from "@dxos/echo";
|
|
1139
|
-
import { getSpace } from "@dxos/react-client/echo";
|
|
1140
|
-
import { Message as Message2, Transcript } from "@dxos/types";
|
|
1141
|
-
import { BlueprintDefinition, OperationHandler, ReactSurface, Transcriber as Transcriber2 } from "#capabilities";
|
|
1142
|
-
import { meta as meta3 } from "#meta";
|
|
1143
|
-
|
|
1144
|
-
// src/translations.ts
|
|
1145
|
-
import { meta as meta2 } from "#meta";
|
|
1146
|
-
var translations = [
|
|
1147
|
-
{
|
|
1148
|
-
"en-US": {
|
|
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"
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
];
|
|
1160
|
-
|
|
1161
|
-
// src/TranscriptionPlugin.tsx
|
|
1162
|
-
var TranscriptionPlugin = Plugin.define(meta3).pipe(AppPlugin.addBlueprintDefinitionModule({
|
|
1163
|
-
activate: BlueprintDefinition
|
|
1164
|
-
}), AppPlugin.addMetadataModule({
|
|
1165
|
-
metadata: {
|
|
1166
|
-
id: Transcript.Transcript.typename,
|
|
1167
|
-
metadata: {
|
|
1168
|
-
icon: Annotation.IconAnnotation.get(Transcript.Transcript).pipe(Option.getOrThrow).icon,
|
|
1169
|
-
iconHue: Annotation.IconAnnotation.get(Transcript.Transcript).pipe(Option.getOrThrow).hue ?? "white",
|
|
1170
|
-
// TODO(wittjosiah): Factor out. Artifact? Separate capability?
|
|
1171
|
-
getTextContent: async (transcript) => {
|
|
1172
|
-
const space = getSpace(transcript);
|
|
1173
|
-
const members = space?.members.get().map((member) => member.identity) ?? [];
|
|
1174
|
-
const queue = space?.queues.get(transcript.queue.dxn);
|
|
1175
|
-
await queue?.refresh();
|
|
1176
|
-
const content = queue?.objects.filter((message) => Obj2.instanceOf(Message2.Message, message)).flatMap((message, index) => renderByline(members)(message, index)).join("\n\n");
|
|
1177
|
-
return content;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}), AppPlugin.addOperationHandlerModule({
|
|
1182
|
-
activate: OperationHandler
|
|
1183
|
-
}), AppPlugin.addSchemaModule({
|
|
1184
|
-
schema: [
|
|
1185
|
-
Transcript.Transcript
|
|
1186
|
-
]
|
|
1187
|
-
}), AppPlugin.addSurfaceModule({
|
|
1188
|
-
activate: ReactSurface
|
|
1189
|
-
}), AppPlugin.addTranslationsModule({
|
|
1190
|
-
translations
|
|
1191
|
-
}), Plugin.addModule({
|
|
1192
|
-
id: "transcription",
|
|
1193
|
-
activatesOn: AppActivationEvents.SetupAppGraph,
|
|
1194
|
-
activate: Transcriber2
|
|
1195
|
-
}), Plugin.make);
|
|
457
|
+
// src/index.ts
|
|
458
|
+
var TranscriptionPlugin = Plugin.lazy(meta, () => import("#plugin"));
|
|
1196
459
|
export {
|
|
1197
460
|
MediaStreamRecorder,
|
|
1198
461
|
Transcriber,
|
|
1199
|
-
Transcription,
|
|
1200
462
|
TranscriptionCapabilities,
|
|
1201
463
|
TranscriptionManager,
|
|
1202
464
|
TranscriptionPlugin,
|
|
465
|
+
alignWhisperSegments,
|
|
1203
466
|
meta,
|
|
1204
|
-
renderByline,
|
|
1205
467
|
useAudioFile,
|
|
1206
468
|
useAudioTrack,
|
|
1207
469
|
useQueueModelAdapter,
|