@corti/dictation-web 0.1.5 → 0.1.7
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/README.md +108 -101
- package/dist/CortiDictation.d.ts +10 -4
- package/dist/CortiDictation.js +82 -53
- package/dist/CortiDictation.js.map +1 -1
- package/dist/DictationService.d.ts +0 -1
- package/dist/DictationService.js +27 -8
- package/dist/DictationService.js.map +1 -1
- package/dist/RecorderManager.d.ts +5 -2
- package/dist/RecorderManager.js +62 -15
- package/dist/RecorderManager.js.map +1 -1
- package/dist/bundle.js +206 -69
- package/dist/components/settings-menu.d.ts +5 -2
- package/dist/components/settings-menu.js +30 -14
- package/dist/components/settings-menu.js.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/styles/ComponentStyles.js +1 -0
- package/dist/styles/ComponentStyles.js.map +1 -1
- package/dist/styles/callout.js +29 -22
- package/dist/styles/callout.js.map +1 -1
- package/dist/types.d.ts +9 -3
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +25 -3
- package/dist/utils.js.map +1 -1
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -1,101 +1,108 @@
|
|
|
1
|
-
# Corti Dictation SDK
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The **Corti Dictation SDK** is a web component that enables real-time speech-to-text dictation using Corti's Dictation API. It provides a simple interface for capturing audio, streaming it to the API, and handling transcripts.
|
|
6
|
-
|
|
7
|
-
> **Note:** OAuth 2.0 authentication is not handled by this SDK. The client must provide an API key or authorization token before using the component.
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
Include the SDK in your project by importing the JavaScript module:
|
|
12
|
-
|
|
13
|
-
```html
|
|
14
|
-
npm i @corti/dictation-web
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
| `
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
1
|
+
# Corti Dictation SDK
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The **Corti Dictation SDK** is a web component that enables real-time speech-to-text dictation using Corti's Dictation API. It provides a simple interface for capturing audio, streaming it to the API, and handling transcripts.
|
|
6
|
+
|
|
7
|
+
> **Note:** OAuth 2.0 authentication is not handled by this SDK. The client must provide an API key or authorization token before using the component.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Include the SDK in your project by importing the JavaScript module:
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
npm i @corti/dictation-web
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Alternatively, use a CDN to start quickly (not recommended).
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<script
|
|
21
|
+
src="https://cdn.jsdelivr.net/npm/@corti/dictation-web/dist/bundle.min.js"
|
|
22
|
+
preload
|
|
23
|
+
type="module"
|
|
24
|
+
></script>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### Demo
|
|
30
|
+
|
|
31
|
+
🚀 [Hosted Demo](https://codepen.io/hccullen/pen/OPJmxQR)
|
|
32
|
+
|
|
33
|
+
### Basic Example
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<!DOCTYPE html>
|
|
37
|
+
<html lang="en">
|
|
38
|
+
<body>
|
|
39
|
+
<script
|
|
40
|
+
src="https://cdn.jsdelivr.net/npm/@corti/dictation-web/dist/bundle.min.js"
|
|
41
|
+
preload
|
|
42
|
+
type="module"
|
|
43
|
+
></script>
|
|
44
|
+
<corti-dictation></corti-dictation>
|
|
45
|
+
<textarea
|
|
46
|
+
id="transcript"
|
|
47
|
+
placeholder="Transcript will appear here..."
|
|
48
|
+
></textarea>
|
|
49
|
+
|
|
50
|
+
<script>
|
|
51
|
+
const dictation = document.getElementById('transcript');
|
|
52
|
+
dictation.authToken = "YOUR_AUTH_TOKEN" // Note: Never hardcode tokens
|
|
53
|
+
// Listen for events
|
|
54
|
+
dictationEl.addEventListener('transcript', e => {
|
|
55
|
+
document.getElementById('transcript').value += e.detail.data.text + ' ';
|
|
56
|
+
});
|
|
57
|
+
</script>
|
|
58
|
+
</body>
|
|
59
|
+
</html>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Reference
|
|
63
|
+
|
|
64
|
+
### Properties
|
|
65
|
+
|
|
66
|
+
| Property | Type | Description |
|
|
67
|
+
| ----------------- | ------ | ---------------------------------------------------- |
|
|
68
|
+
| `devices` | Array | List of available recording devices. |
|
|
69
|
+
| `selectedDevice` | Object | The selected device used for recording (MediaDeviceInfo). |
|
|
70
|
+
| `recordingState` | String | Current state of recording (`stopped`, `recording`, `initializing` and `stopping`, ). |
|
|
71
|
+
| `dictationConfig` | Object | Configuration settings for dictation. |
|
|
72
|
+
| `authToken` | String | Authentication token from OAuth server |
|
|
73
|
+
| `debug_displayAudio` | Boolean | Overrides any device selection and instead uses getDisplayMedia to stream system audio. SHould only be used for debugging |
|
|
74
|
+
|
|
75
|
+
### Methods
|
|
76
|
+
|
|
77
|
+
| Method | Description |
|
|
78
|
+
| ------------------- | -------------------------- |
|
|
79
|
+
| `toggleRecording()` | Starts or stops recording. |
|
|
80
|
+
|
|
81
|
+
### Events
|
|
82
|
+
|
|
83
|
+
| Event | Description |
|
|
84
|
+
| -------------------------- | --------------------------------------------------------------------------------------------- |
|
|
85
|
+
| `ready` | Fired once the component is ready. |
|
|
86
|
+
| `recording-state-changed` | Fired when the recording state changes. `detail.state` contains the new state. |
|
|
87
|
+
| `recording-devices-changed` | Fired when the user switches recording devices or the list of recording devices changes. `detail.devices` contains the full devices list. `detail.selectedDevice` contains the current selected device. |
|
|
88
|
+
| `transcript` | Fired when a new transcript is received. `detail.data.text` contains the transcribed text. |
|
|
89
|
+
| `command` | Fired whenever a new command is detected. |
|
|
90
|
+
| `audio-level-changed` | Fired when the input audio level changes. `detail.audioLevel` contains the new level. |
|
|
91
|
+
| `error` | Fired on error. `detail` contains the full error. |
|
|
92
|
+
|
|
93
|
+
## Authentication
|
|
94
|
+
|
|
95
|
+
This SDK does not handle OAuth 2.0 authentication. The client must provide an API key or access token as a string in `authToken`.
|
|
96
|
+
|
|
97
|
+
## Notes
|
|
98
|
+
|
|
99
|
+
- Works in modern browsers that support Web Components and MediaRecorder API.
|
|
100
|
+
- Supports dark and light mode based on browser preference.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
This SDK is not licensed.
|
|
105
|
+
|
|
106
|
+
## Support
|
|
107
|
+
|
|
108
|
+
For issues or questions, contact **Corti Support** at [support@corti.ai](mailto:support@corti.ai).
|
package/dist/CortiDictation.d.ts
CHANGED
|
@@ -2,19 +2,25 @@ import { LitElement } from 'lit';
|
|
|
2
2
|
import './components/settings-menu.js';
|
|
3
3
|
import './components/audio-visualiser.js';
|
|
4
4
|
import './icons/icons.js';
|
|
5
|
-
import type { DictationConfig } from './types.js';
|
|
5
|
+
import type { DictationConfig, RecordingState } from './types.js';
|
|
6
6
|
export declare class CortiDictation extends LitElement {
|
|
7
7
|
static styles: import("lit").CSSResult[];
|
|
8
|
-
devices: MediaDeviceInfo[];
|
|
9
|
-
recordingState: string;
|
|
10
8
|
dictationConfig: DictationConfig;
|
|
11
9
|
authToken: string | undefined;
|
|
10
|
+
debug_displayAudio: boolean;
|
|
12
11
|
private _audioLevel;
|
|
12
|
+
private _recordingState;
|
|
13
|
+
private _selectedDevice;
|
|
14
|
+
private _devices;
|
|
13
15
|
private recorderManager;
|
|
14
16
|
connectedCallback(): Promise<void>;
|
|
15
17
|
toggleRecording(): void;
|
|
18
|
+
get selectedDevice(): MediaDeviceInfo | null;
|
|
19
|
+
get recordingState(): RecordingState;
|
|
20
|
+
get devices(): MediaDeviceInfo[];
|
|
21
|
+
setRecordingDevice(device: MediaDeviceInfo): Promise<void>;
|
|
16
22
|
_toggleRecording(): void;
|
|
17
|
-
|
|
23
|
+
_onRecordingDevicesChanged(event: Event): Promise<void>;
|
|
18
24
|
render(): import("lit-html").TemplateResult<1>;
|
|
19
25
|
}
|
|
20
26
|
export default CortiDictation;
|
package/dist/CortiDictation.js
CHANGED
|
@@ -19,20 +19,29 @@ import CalloutStyles from './styles/callout.js';
|
|
|
19
19
|
export class CortiDictation extends LitElement {
|
|
20
20
|
constructor() {
|
|
21
21
|
super(...arguments);
|
|
22
|
-
this.devices = [];
|
|
23
|
-
this.recordingState = 'stopped';
|
|
24
22
|
this.dictationConfig = DEFAULT_DICTATION_CONFIG;
|
|
23
|
+
this.debug_displayAudio = false;
|
|
25
24
|
this._audioLevel = 0;
|
|
25
|
+
this._recordingState = 'stopped';
|
|
26
|
+
this._devices = [];
|
|
26
27
|
this.recorderManager = new RecorderManager();
|
|
27
28
|
}
|
|
28
29
|
async connectedCallback() {
|
|
29
30
|
super.connectedCallback();
|
|
30
|
-
await this.recorderManager.initialize();
|
|
31
|
-
|
|
31
|
+
const devices = await this.recorderManager.initialize();
|
|
32
|
+
if (devices.selectedDevice) {
|
|
33
|
+
this._selectedDevice = this.recorderManager.selectedDevice;
|
|
34
|
+
this._devices = this.recorderManager.devices;
|
|
35
|
+
this.dispatchEvent(new CustomEvent('ready'));
|
|
36
|
+
}
|
|
32
37
|
// Map event names to any extra handling logic
|
|
33
38
|
const eventHandlers = {
|
|
34
39
|
'recording-state-changed': e => {
|
|
35
|
-
this.
|
|
40
|
+
this._recordingState = e.detail.state;
|
|
41
|
+
},
|
|
42
|
+
'devices-changed': () => {
|
|
43
|
+
this._devices = [...this.recorderManager.devices];
|
|
44
|
+
this.requestUpdate();
|
|
36
45
|
},
|
|
37
46
|
'audio-level-changed': e => {
|
|
38
47
|
this._audioLevel = e.detail.audioLevel;
|
|
@@ -41,9 +50,12 @@ export class CortiDictation extends LitElement {
|
|
|
41
50
|
};
|
|
42
51
|
const eventsToRelay = [
|
|
43
52
|
'recording-state-changed',
|
|
53
|
+
'recording-devices-changed',
|
|
44
54
|
'audio-level-changed',
|
|
45
55
|
'error',
|
|
46
56
|
'transcript',
|
|
57
|
+
'command',
|
|
58
|
+
'ready',
|
|
47
59
|
];
|
|
48
60
|
eventsToRelay.forEach(eventName => {
|
|
49
61
|
this.recorderManager.addEventListener(eventName, (e) => {
|
|
@@ -64,89 +76,106 @@ export class CortiDictation extends LitElement {
|
|
|
64
76
|
toggleRecording() {
|
|
65
77
|
this._toggleRecording();
|
|
66
78
|
}
|
|
67
|
-
|
|
79
|
+
get selectedDevice() {
|
|
80
|
+
return this.recorderManager.selectedDevice || null;
|
|
81
|
+
}
|
|
82
|
+
get recordingState() {
|
|
83
|
+
return this._recordingState;
|
|
84
|
+
}
|
|
85
|
+
get devices() {
|
|
86
|
+
return this._devices;
|
|
87
|
+
}
|
|
88
|
+
async setRecordingDevice(device) {
|
|
89
|
+
this.recorderManager.selectedDevice = device;
|
|
90
|
+
this._selectedDevice = device;
|
|
68
91
|
if (!this.authToken)
|
|
69
92
|
return;
|
|
70
|
-
if (this.
|
|
71
|
-
this.recorderManager.stopRecording();
|
|
72
|
-
|
|
73
|
-
else if (this.recordingState === 'stopped') {
|
|
74
|
-
this.recorderManager.startRecording({
|
|
93
|
+
if (this._recordingState === 'recording') {
|
|
94
|
+
await this.recorderManager.stopRecording();
|
|
95
|
+
await this.recorderManager.startRecording({
|
|
75
96
|
dictationConfig: this.dictationConfig,
|
|
76
97
|
authToken: this.authToken,
|
|
77
98
|
});
|
|
78
99
|
}
|
|
79
100
|
}
|
|
80
|
-
|
|
81
|
-
async _onRecordingDeviceChanged(event) {
|
|
82
|
-
const customEvent = event;
|
|
101
|
+
_toggleRecording() {
|
|
83
102
|
if (!this.authToken)
|
|
84
103
|
return;
|
|
85
|
-
this.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
if (this._recordingState === 'recording') {
|
|
105
|
+
this.recorderManager.stopRecording();
|
|
106
|
+
}
|
|
107
|
+
else if (this._recordingState === 'stopped') {
|
|
108
|
+
this.recorderManager.startRecording({
|
|
89
109
|
dictationConfig: this.dictationConfig,
|
|
90
110
|
authToken: this.authToken,
|
|
111
|
+
debug_displayAudio: this.debug_displayAudio,
|
|
91
112
|
});
|
|
92
113
|
}
|
|
93
114
|
}
|
|
115
|
+
// Handle device change events if needed
|
|
116
|
+
async _onRecordingDevicesChanged(event) {
|
|
117
|
+
const customEvent = event;
|
|
118
|
+
this.setRecordingDevice(customEvent.detail.selectedDevice);
|
|
119
|
+
}
|
|
94
120
|
render() {
|
|
95
121
|
const isConfigured = this.authToken;
|
|
96
122
|
if (!isConfigured) {
|
|
97
|
-
return html `
|
|
98
|
-
<div class="wrapper">
|
|
99
|
-
<div class="callout red
|
|
100
|
-
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
123
|
+
return html `
|
|
124
|
+
<div class="wrapper">
|
|
125
|
+
<div class="callout red small">No Auth Token</div>
|
|
126
|
+
</div>
|
|
103
127
|
`;
|
|
104
128
|
}
|
|
105
|
-
const isLoading = this.
|
|
106
|
-
this.
|
|
107
|
-
const isRecording = this.
|
|
108
|
-
return html `
|
|
109
|
-
<div class="wrapper">
|
|
110
|
-
<button
|
|
111
|
-
@click=${this._toggleRecording}
|
|
112
|
-
class=${isRecording ? 'red' : 'accent'}
|
|
113
|
-
>
|
|
129
|
+
const isLoading = this._recordingState === 'initializing' ||
|
|
130
|
+
this._recordingState === 'stopping';
|
|
131
|
+
const isRecording = this._recordingState === 'recording';
|
|
132
|
+
return html `
|
|
133
|
+
<div class="wrapper">
|
|
134
|
+
<button
|
|
135
|
+
@click=${this._toggleRecording}
|
|
136
|
+
class=${isRecording ? 'red' : 'accent'}
|
|
137
|
+
>
|
|
114
138
|
${isLoading
|
|
115
139
|
? html `<icon-loading-spinner></icon-loading-spinner>`
|
|
116
140
|
: isRecording
|
|
117
141
|
? html `<icon-recording></icon-recording>`
|
|
118
|
-
: html `<icon-mic-on></icon-mic-on>`}
|
|
119
|
-
<audio-visualiser
|
|
120
|
-
.level=${this._audioLevel}
|
|
121
|
-
.active=${isRecording}
|
|
122
|
-
></audio-visualiser>
|
|
123
|
-
</button>
|
|
124
|
-
|
|
125
|
-
<settings-menu
|
|
126
|
-
.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</div>
|
|
142
|
+
: html `<icon-mic-on></icon-mic-on>`}
|
|
143
|
+
<audio-visualiser
|
|
144
|
+
.level=${this._audioLevel}
|
|
145
|
+
.active=${isRecording}
|
|
146
|
+
></audio-visualiser>
|
|
147
|
+
</button>
|
|
148
|
+
|
|
149
|
+
<settings-menu
|
|
150
|
+
.selectedDevice=${this._selectedDevice}
|
|
151
|
+
?settingsDisabled=${this._recordingState !== 'stopped'}
|
|
152
|
+
@recording-devices-changed=${this._onRecordingDevicesChanged}
|
|
153
|
+
></settings-menu>
|
|
154
|
+
</div>
|
|
132
155
|
`;
|
|
133
156
|
}
|
|
134
157
|
}
|
|
135
158
|
CortiDictation.styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyles];
|
|
136
|
-
__decorate([
|
|
137
|
-
property({ type: Array })
|
|
138
|
-
], CortiDictation.prototype, "devices", void 0);
|
|
139
|
-
__decorate([
|
|
140
|
-
property({ type: String, reflect: true })
|
|
141
|
-
], CortiDictation.prototype, "recordingState", void 0);
|
|
142
159
|
__decorate([
|
|
143
160
|
property({ type: Object })
|
|
144
161
|
], CortiDictation.prototype, "dictationConfig", void 0);
|
|
145
162
|
__decorate([
|
|
146
163
|
property({ type: String })
|
|
147
164
|
], CortiDictation.prototype, "authToken", void 0);
|
|
165
|
+
__decorate([
|
|
166
|
+
property({ type: Boolean })
|
|
167
|
+
], CortiDictation.prototype, "debug_displayAudio", void 0);
|
|
148
168
|
__decorate([
|
|
149
169
|
state()
|
|
150
170
|
], CortiDictation.prototype, "_audioLevel", void 0);
|
|
171
|
+
__decorate([
|
|
172
|
+
state()
|
|
173
|
+
], CortiDictation.prototype, "_recordingState", void 0);
|
|
174
|
+
__decorate([
|
|
175
|
+
state()
|
|
176
|
+
], CortiDictation.prototype, "_selectedDevice", void 0);
|
|
177
|
+
__decorate([
|
|
178
|
+
state()
|
|
179
|
+
], CortiDictation.prototype, "_devices", void 0);
|
|
151
180
|
export default CortiDictation;
|
|
152
181
|
//# sourceMappingURL=CortiDictation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CortiDictation.js","sourceRoot":"","sources":["../src/CortiDictation.ts"],"names":[],"mappings":"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","sourcesContent":["// corti-dictation.ts\nimport { html, LitElement } from 'lit';\nimport { property, state } from 'lit/decorators.js';\nimport { RecorderManager } from './RecorderManager.js';\nimport './components/settings-menu.js';\nimport './components/audio-visualiser.js';\nimport './icons/icons.js';\nimport ThemeStyles from './styles/theme.js';\nimport ButtonStyles from './styles/buttons.js';\nimport ComponentStyles from './styles/ComponentStyles.js';\n\nimport type { DictationConfig } from './types.js';\nimport { DEFAULT_DICTATION_CONFIG } from './constants.js';\nimport CalloutStyles from './styles/callout.js';\n\nexport class CortiDictation extends LitElement {\n static styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyles];\n\n @property({ type: Array })\n devices: MediaDeviceInfo[] = [];\n\n @property({ type: String, reflect: true })\n recordingState = 'stopped';\n\n @property({ type: Object })\n dictationConfig: DictationConfig = DEFAULT_DICTATION_CONFIG;\n\n @property({ type: String })\n authToken: string | undefined;\n\n @state()\n private _audioLevel = 0;\n\n private recorderManager = new RecorderManager();\n\n async connectedCallback() {\n super.connectedCallback();\n await this.recorderManager.initialize();\n this.devices = this.recorderManager.devices;\n\n // Map event names to any extra handling logic\n const eventHandlers: Record<string, (e: CustomEvent) => void> = {\n 'recording-state-changed': e => {\n this.recordingState = e.detail.state;\n },\n 'audio-level-changed': e => {\n this._audioLevel = e.detail.audioLevel;\n this.requestUpdate();\n },\n };\n\n const eventsToRelay = [\n 'recording-state-changed',\n 'audio-level-changed',\n 'error',\n 'transcript',\n ];\n\n eventsToRelay.forEach(eventName => {\n this.recorderManager.addEventListener(eventName, (e: Event) => {\n const customEvent = e as CustomEvent;\n // Perform any additional handling if defined\n if (eventHandlers[eventName]) {\n eventHandlers[eventName](customEvent);\n }\n // Re-dispatch the event from the component\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail: customEvent.detail,\n bubbles: true,\n composed: true,\n }),\n );\n });\n });\n }\n\n public toggleRecording() {\n this._toggleRecording();\n }\n\n _toggleRecording() {\n if (!this.authToken) return;\n if (this.recordingState === 'recording') {\n this.recorderManager.stopRecording();\n } else if (this.recordingState === 'stopped') {\n this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n authToken: this.authToken,\n });\n }\n }\n\n // Handle device change events if needed\n async _onRecordingDeviceChanged(event: Event) {\n const customEvent = event as CustomEvent;\n if (!this.authToken) return;\n this.recorderManager.selectedDevice = customEvent.detail.deviceId;\n if (this.recordingState === 'recording') {\n await this.recorderManager.stopRecording();\n await this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n authToken: this.authToken,\n });\n }\n }\n\n render() {\n const isConfigured = this.authToken;\n if (!isConfigured) {\n return html`\n <div class=\"wrapper\">\n <div class=\"callout red tiny\">\n Please configure the server settings in the parent component.\n </div>\n </div>\n `;\n }\n\n const isLoading =\n this.recordingState === 'initializing' ||\n this.recordingState === 'stopping';\n const isRecording = this.recordingState === 'recording';\n return html`\n <div class=\"wrapper\">\n <button\n @click=${this._toggleRecording}\n class=${isRecording ? 'red' : 'accent'}\n >\n ${isLoading\n ? html`<icon-loading-spinner></icon-loading-spinner>`\n : isRecording\n ? html`<icon-recording></icon-recording>`\n : html`<icon-mic-on></icon-mic-on>`}\n <audio-visualiser\n .level=${this._audioLevel}\n .active=${isRecording}\n ></audio-visualiser>\n </button>\n\n <settings-menu\n .devices=${this.devices}\n .selectedDevice=${this.recorderManager.selectedDevice}\n ?settingsDisabled=${this.recordingState !== 'stopped'}\n @recording-device-changed=${this._onRecordingDeviceChanged}\n ></settings-menu>\n </div>\n `;\n }\n}\n\nexport default CortiDictation;\n"]}
|
|
1
|
+
{"version":3,"file":"CortiDictation.js","sourceRoot":"","sources":["../src/CortiDictation.ts"],"names":[],"mappings":"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","sourcesContent":["// corti-dictation.ts\r\nimport { html, LitElement } from 'lit';\r\nimport { property, state } from 'lit/decorators.js';\r\nimport { RecorderManager } from './RecorderManager.js';\r\nimport './components/settings-menu.js';\r\nimport './components/audio-visualiser.js';\r\nimport './icons/icons.js';\r\nimport ThemeStyles from './styles/theme.js';\r\nimport ButtonStyles from './styles/buttons.js';\r\nimport ComponentStyles from './styles/ComponentStyles.js';\r\n\r\nimport type { DictationConfig, RecordingState } from './types.js';\r\nimport { DEFAULT_DICTATION_CONFIG } from './constants.js';\r\nimport CalloutStyles from './styles/callout.js';\r\n\r\nexport class CortiDictation extends LitElement {\r\n static styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyles];\r\n\r\n @property({ type: Object })\r\n dictationConfig: DictationConfig = DEFAULT_DICTATION_CONFIG;\r\n\r\n @property({ type: String })\r\n authToken: string | undefined;\r\n\r\n @property({ type: Boolean })\r\n debug_displayAudio: boolean = false;\r\n\r\n @state()\r\n private _audioLevel: number = 0;\r\n\r\n @state()\r\n private _recordingState: RecordingState = 'stopped';\r\n\r\n @state()\r\n private _selectedDevice: MediaDeviceInfo | undefined;\r\n\r\n @state()\r\n private _devices: MediaDeviceInfo[] = [];\r\n\r\n private recorderManager = new RecorderManager();\r\n\r\n async connectedCallback() {\r\n super.connectedCallback();\r\n const devices = await this.recorderManager.initialize();\r\n if (devices.selectedDevice) {\r\n this._selectedDevice = this.recorderManager.selectedDevice;\r\n this._devices = this.recorderManager.devices;\r\n this.dispatchEvent(new CustomEvent('ready'));\r\n }\r\n\r\n // Map event names to any extra handling logic\r\n const eventHandlers: Record<string, (e: CustomEvent) => void> = {\r\n 'recording-state-changed': e => {\r\n this._recordingState = e.detail.state;\r\n },\r\n 'devices-changed': () => {\r\n this._devices = [...this.recorderManager.devices];\r\n this.requestUpdate();\r\n },\r\n 'audio-level-changed': e => {\r\n this._audioLevel = e.detail.audioLevel;\r\n this.requestUpdate();\r\n },\r\n };\r\n\r\n const eventsToRelay = [\r\n 'recording-state-changed',\r\n 'recording-devices-changed',\r\n 'audio-level-changed',\r\n 'error',\r\n 'transcript',\r\n 'command',\r\n 'ready',\r\n ];\r\n\r\n eventsToRelay.forEach(eventName => {\r\n this.recorderManager.addEventListener(eventName, (e: Event) => {\r\n const customEvent = e as CustomEvent;\r\n // Perform any additional handling if defined\r\n if (eventHandlers[eventName]) {\r\n eventHandlers[eventName](customEvent);\r\n }\r\n // Re-dispatch the event from the component\r\n this.dispatchEvent(\r\n new CustomEvent(eventName, {\r\n detail: customEvent.detail,\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n });\r\n });\r\n }\r\n\r\n public toggleRecording() {\r\n this._toggleRecording();\r\n }\r\n\r\n public get selectedDevice(): MediaDeviceInfo | null {\r\n return this.recorderManager.selectedDevice || null;\r\n }\r\n\r\n public get recordingState(): RecordingState {\r\n return this._recordingState;\r\n }\r\n\r\n public get devices(): MediaDeviceInfo[] {\r\n return this._devices;\r\n }\r\n\r\n public async setRecordingDevice(device: MediaDeviceInfo) {\r\n this.recorderManager.selectedDevice = device;\r\n this._selectedDevice = device;\r\n if (!this.authToken) return;\r\n if (this._recordingState === 'recording') {\r\n await this.recorderManager.stopRecording();\r\n await this.recorderManager.startRecording({\r\n dictationConfig: this.dictationConfig,\r\n authToken: this.authToken,\r\n });\r\n }\r\n }\r\n\r\n _toggleRecording() {\r\n if (!this.authToken) return;\r\n if (this._recordingState === 'recording') {\r\n this.recorderManager.stopRecording();\r\n } else if (this._recordingState === 'stopped') {\r\n this.recorderManager.startRecording({\r\n dictationConfig: this.dictationConfig,\r\n authToken: this.authToken,\r\n debug_displayAudio: this.debug_displayAudio,\r\n });\r\n }\r\n }\r\n\r\n // Handle device change events if needed\r\n async _onRecordingDevicesChanged(event: Event) {\r\n const customEvent = event as CustomEvent;\r\n this.setRecordingDevice(customEvent.detail.selectedDevice);\r\n }\r\n\r\n render() {\r\n const isConfigured = this.authToken;\r\n if (!isConfigured) {\r\n return html`\r\n <div class=\"wrapper\">\r\n <div class=\"callout red small\">No Auth Token</div>\r\n </div>\r\n `;\r\n }\r\n\r\n const isLoading =\r\n this._recordingState === 'initializing' ||\r\n this._recordingState === 'stopping';\r\n const isRecording = this._recordingState === 'recording';\r\n return html`\r\n <div class=\"wrapper\">\r\n <button\r\n @click=${this._toggleRecording}\r\n class=${isRecording ? 'red' : 'accent'}\r\n >\r\n ${isLoading\r\n ? html`<icon-loading-spinner></icon-loading-spinner>`\r\n : isRecording\r\n ? html`<icon-recording></icon-recording>`\r\n : html`<icon-mic-on></icon-mic-on>`}\r\n <audio-visualiser\r\n .level=${this._audioLevel}\r\n .active=${isRecording}\r\n ></audio-visualiser>\r\n </button>\r\n\r\n <settings-menu\r\n .selectedDevice=${this._selectedDevice}\r\n ?settingsDisabled=${this._recordingState !== 'stopped'}\r\n @recording-devices-changed=${this._onRecordingDevicesChanged}\r\n ></settings-menu>\r\n </div>\r\n `;\r\n }\r\n}\r\n\r\nexport default CortiDictation;\r\n"]}
|
|
@@ -4,7 +4,6 @@ export declare class DictationService extends EventTarget {
|
|
|
4
4
|
private webSocket;
|
|
5
5
|
private authToken;
|
|
6
6
|
private dictationConfig;
|
|
7
|
-
private serverConfig;
|
|
8
7
|
constructor(mediaStream: MediaStream, { dictationConfig, authToken, }: {
|
|
9
8
|
dictationConfig: DictationConfig;
|
|
10
9
|
authToken: string;
|
package/dist/DictationService.js
CHANGED
|
@@ -10,7 +10,6 @@ export class DictationService extends EventTarget {
|
|
|
10
10
|
if (!config) {
|
|
11
11
|
throw new Error('Invalid token');
|
|
12
12
|
}
|
|
13
|
-
this.serverConfig = config;
|
|
14
13
|
this.mediaRecorder.ondataavailable = event => {
|
|
15
14
|
if (this.webSocket?.readyState === WebSocket.OPEN) {
|
|
16
15
|
this.webSocket.send(event.data);
|
|
@@ -25,7 +24,16 @@ export class DictationService extends EventTarget {
|
|
|
25
24
|
}));
|
|
26
25
|
}
|
|
27
26
|
startRecording() {
|
|
28
|
-
const
|
|
27
|
+
const serverConfig = decodeToken(this.authToken);
|
|
28
|
+
if (!serverConfig) {
|
|
29
|
+
this.dispatchEvent(new CustomEvent('error', {
|
|
30
|
+
detail: 'Invalid token',
|
|
31
|
+
bubbles: true,
|
|
32
|
+
composed: true,
|
|
33
|
+
}));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;
|
|
29
37
|
this.webSocket = new WebSocket(url);
|
|
30
38
|
this.webSocket.onopen = () => {
|
|
31
39
|
this.webSocket.send(JSON.stringify({
|
|
@@ -35,11 +43,22 @@ export class DictationService extends EventTarget {
|
|
|
35
43
|
};
|
|
36
44
|
this.webSocket.onmessage = event => {
|
|
37
45
|
const message = JSON.parse(event.data);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
switch (message.type) {
|
|
47
|
+
case 'CONFIG_ACCEPTED':
|
|
48
|
+
this.mediaRecorder.start(250);
|
|
49
|
+
break;
|
|
50
|
+
case 'CONFIG_DENIED':
|
|
51
|
+
this.dispatchCustomEvent('error', message);
|
|
52
|
+
return this.stopRecording();
|
|
53
|
+
case 'transcript':
|
|
54
|
+
this.dispatchCustomEvent('transcript', message);
|
|
55
|
+
break;
|
|
56
|
+
case 'command':
|
|
57
|
+
this.dispatchCustomEvent('command', message);
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
console.warn(`Unhandled message type: ${message.type}`);
|
|
61
|
+
break;
|
|
43
62
|
}
|
|
44
63
|
};
|
|
45
64
|
this.webSocket.onerror = event => {
|
|
@@ -50,7 +69,7 @@ export class DictationService extends EventTarget {
|
|
|
50
69
|
};
|
|
51
70
|
}
|
|
52
71
|
async stopRecording() {
|
|
53
|
-
this.mediaRecorder
|
|
72
|
+
this.mediaRecorder?.stop();
|
|
54
73
|
if (this.webSocket?.readyState === WebSocket.OPEN) {
|
|
55
74
|
this.webSocket.send(JSON.stringify({ type: 'end' }));
|
|
56
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DictationService.js","sourceRoot":"","sources":["../src/DictationService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,OAAO,gBAAiB,SAAQ,WAAW;
|
|
1
|
+
{"version":3,"file":"DictationService.js","sourceRoot":"","sources":["../src/DictationService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DictationConfig, ServerConfig } from './types.js';\r\nimport { decodeToken } from './utils.js';\r\n\r\nexport class DictationService extends EventTarget {\r\n private mediaRecorder: MediaRecorder;\r\n private webSocket!: WebSocket;\r\n private authToken: string;\r\n private dictationConfig: DictationConfig;\r\n\r\n constructor(\r\n mediaStream: MediaStream,\r\n {\r\n dictationConfig,\r\n authToken,\r\n }: { dictationConfig: DictationConfig; authToken: string },\r\n ) {\r\n super();\r\n this.mediaRecorder = new MediaRecorder(mediaStream);\r\n this.authToken = authToken;\r\n this.dictationConfig = dictationConfig;\r\n\r\n // Decode token during construction.\r\n const config = decodeToken(this.authToken);\r\n if (!config) {\r\n throw new Error('Invalid token');\r\n }\r\n\r\n this.mediaRecorder.ondataavailable = event => {\r\n if (this.webSocket?.readyState === WebSocket.OPEN) {\r\n this.webSocket.send(event.data);\r\n }\r\n };\r\n }\r\n\r\n private dispatchCustomEvent(eventName: string, detail?: unknown): void {\r\n this.dispatchEvent(\r\n new CustomEvent(eventName, {\r\n detail,\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n\r\n public startRecording() {\r\n const serverConfig: ServerConfig | undefined = decodeToken(this.authToken);\r\n if (!serverConfig) {\r\n this.dispatchEvent(\r\n new CustomEvent('error', {\r\n detail: 'Invalid token',\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n return;\r\n }\r\n\r\n const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;\r\n this.webSocket = new WebSocket(url);\r\n\r\n this.webSocket.onopen = () => {\r\n this.webSocket.send(\r\n JSON.stringify({\r\n type: 'config',\r\n configuration: this.dictationConfig,\r\n }),\r\n );\r\n };\r\n\r\n this.webSocket.onmessage = event => {\r\n const message = JSON.parse(event.data);\r\n switch (message.type) {\r\n case 'CONFIG_ACCEPTED':\r\n this.mediaRecorder.start(250);\r\n break;\r\n case 'CONFIG_DENIED':\r\n this.dispatchCustomEvent('error', message);\r\n return this.stopRecording();\r\n case 'transcript':\r\n this.dispatchCustomEvent('transcript', message);\r\n break;\r\n case 'command':\r\n this.dispatchCustomEvent('command', message);\r\n break;\r\n default:\r\n console.warn(`Unhandled message type: ${message.type}`);\r\n break;\r\n }\r\n };\r\n\r\n this.webSocket.onerror = event => {\r\n this.dispatchCustomEvent('error', event);\r\n };\r\n\r\n this.webSocket.onclose = event => {\r\n this.dispatchCustomEvent('stream-closed', event);\r\n };\r\n }\r\n\r\n public async stopRecording(): Promise<void> {\r\n this.mediaRecorder?.stop();\r\n\r\n if (this.webSocket?.readyState === WebSocket.OPEN) {\r\n this.webSocket.send(JSON.stringify({ type: 'end' }));\r\n }\r\n\r\n const timeout: NodeJS.Timeout = setTimeout(() => {\r\n if (this.webSocket?.readyState === WebSocket.OPEN) {\r\n this.webSocket.close();\r\n }\r\n }, 10000);\r\n\r\n this.webSocket.onclose = () => {\r\n this.webSocket?.close();\r\n clearTimeout(timeout);\r\n };\r\n }\r\n}\r\n"]}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import type { DictationConfig, RecordingState } from './types.js';
|
|
2
2
|
export declare class RecorderManager extends EventTarget {
|
|
3
3
|
devices: MediaDeviceInfo[];
|
|
4
|
-
selectedDevice:
|
|
4
|
+
selectedDevice: MediaDeviceInfo | undefined;
|
|
5
5
|
recordingState: RecordingState;
|
|
6
6
|
private _mediaStream;
|
|
7
7
|
private _audioService;
|
|
8
8
|
private _dictationService;
|
|
9
9
|
private _visualiserInterval?;
|
|
10
|
+
constructor();
|
|
10
11
|
initialize(): Promise<{
|
|
11
12
|
devices: MediaDeviceInfo[];
|
|
12
|
-
|
|
13
|
+
selectedDevice: MediaDeviceInfo | undefined;
|
|
13
14
|
}>;
|
|
14
15
|
private dispatchCustomEvent;
|
|
16
|
+
private handleDevicesChange;
|
|
15
17
|
startRecording(params: {
|
|
16
18
|
dictationConfig: DictationConfig;
|
|
17
19
|
authToken: string;
|
|
20
|
+
debug_displayAudio?: boolean;
|
|
18
21
|
}): Promise<void>;
|
|
19
22
|
stopRecording(): Promise<void>;
|
|
20
23
|
private _updateRecordingState;
|