@corti/dictation-web 0.1.7 → 0.1.8

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 CHANGED
@@ -13,6 +13,12 @@ Include the SDK in your project by importing the JavaScript module:
13
13
  ```html
14
14
  npm i @corti/dictation-web
15
15
  ```
16
+ Then import the module like so:
17
+
18
+ ```js
19
+ // Import the Corti Dictation SDK
20
+ import "@corti/dictation-web";
21
+ ```
16
22
 
17
23
  Alternatively, use a CDN to start quickly (not recommended).
18
24
 
@@ -36,11 +42,6 @@ Alternatively, use a CDN to start quickly (not recommended).
36
42
  <!DOCTYPE html>
37
43
  <html lang="en">
38
44
  <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
45
  <corti-dictation></corti-dictation>
45
46
  <textarea
46
47
  id="transcript"
@@ -48,8 +49,9 @@ Alternatively, use a CDN to start quickly (not recommended).
48
49
  ></textarea>
49
50
 
50
51
  <script>
52
+ import "@corti/dictation-web";
51
53
  const dictation = document.getElementById('transcript');
52
- dictation.authToken = "YOUR_AUTH_TOKEN" // Note: Never hardcode tokens
54
+ dictation.setAccessToken("YOUR_AUTH_TOKEN") // Note: Never hardcode tokens
53
55
  // Listen for events
54
56
  dictationEl.addEventListener('transcript', e => {
55
57
  document.getElementById('transcript').value += e.detail.data.text + ' ';
@@ -69,7 +71,6 @@ Alternatively, use a CDN to start quickly (not recommended).
69
71
  | `selectedDevice` | Object | The selected device used for recording (MediaDeviceInfo). |
70
72
  | `recordingState` | String | Current state of recording (`stopped`, `recording`, `initializing` and `stopping`, ). |
71
73
  | `dictationConfig` | Object | Configuration settings for dictation. |
72
- | `authToken` | String | Authentication token from OAuth server |
73
74
  | `debug_displayAudio` | Boolean | Overrides any device selection and instead uses getDisplayMedia to stream system audio. SHould only be used for debugging |
74
75
 
75
76
  ### Methods
@@ -77,6 +78,7 @@ Alternatively, use a CDN to start quickly (not recommended).
77
78
  | Method | Description |
78
79
  | ------------------- | -------------------------- |
79
80
  | `toggleRecording()` | Starts or stops recording. |
81
+ | `setAccessToken(access_token: string)` | Set the latest access token. This will return the server config. |
80
82
 
81
83
  ### Events
82
84
 
@@ -92,12 +94,10 @@ Alternatively, use a CDN to start quickly (not recommended).
92
94
 
93
95
  ## Authentication
94
96
 
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
97
+ This SDK does not handle OAuth 2.0 authentication. The client must provide an API key or access token as a string using `setAccessToken`.
98
98
 
99
- - Works in modern browsers that support Web Components and MediaRecorder API.
100
- - Supports dark and light mode based on browser preference.
99
+ ## Styling UI
100
+ If you use the. The UI supports dark and light mode based on browser preference. Alternatively, you can override the CSS variables used in the web component. Refer to our [Styling Guide](docs/styling.md).
101
101
 
102
102
  ## License
103
103
 
@@ -6,8 +6,8 @@ import type { DictationConfig, RecordingState } from './types.js';
6
6
  export declare class CortiDictation extends LitElement {
7
7
  static styles: import("lit").CSSResult[];
8
8
  dictationConfig: DictationConfig;
9
- authToken: string | undefined;
10
9
  debug_displayAudio: boolean;
10
+ private _serverConfig;
11
11
  private _audioLevel;
12
12
  private _recordingState;
13
13
  private _selectedDevice;
@@ -15,6 +15,11 @@ export declare class CortiDictation extends LitElement {
15
15
  private recorderManager;
16
16
  connectedCallback(): Promise<void>;
17
17
  toggleRecording(): void;
18
+ setAccessToken(token: string): {
19
+ environment: string;
20
+ tenant: string;
21
+ accessToken: string;
22
+ };
18
23
  get selectedDevice(): MediaDeviceInfo | null;
19
24
  get recordingState(): RecordingState;
20
25
  get devices(): MediaDeviceInfo[];
@@ -16,6 +16,7 @@ import ButtonStyles from './styles/buttons.js';
16
16
  import ComponentStyles from './styles/ComponentStyles.js';
17
17
  import { DEFAULT_DICTATION_CONFIG } from './constants.js';
18
18
  import CalloutStyles from './styles/callout.js';
19
+ import { decodeToken } from './utils.js';
19
20
  export class CortiDictation extends LitElement {
20
21
  constructor() {
21
22
  super(...arguments);
@@ -76,6 +77,14 @@ export class CortiDictation extends LitElement {
76
77
  toggleRecording() {
77
78
  this._toggleRecording();
78
79
  }
80
+ setAccessToken(token) {
81
+ const decoded = decodeToken(token);
82
+ if (!decoded) {
83
+ throw new Error('Invalid token');
84
+ }
85
+ this._serverConfig = decoded;
86
+ return decoded;
87
+ }
79
88
  get selectedDevice() {
80
89
  return this.recorderManager.selectedDevice || null;
81
90
  }
@@ -88,18 +97,18 @@ export class CortiDictation extends LitElement {
88
97
  async setRecordingDevice(device) {
89
98
  this.recorderManager.selectedDevice = device;
90
99
  this._selectedDevice = device;
91
- if (!this.authToken)
100
+ if (!this._serverConfig)
92
101
  return;
93
102
  if (this._recordingState === 'recording') {
94
103
  await this.recorderManager.stopRecording();
95
104
  await this.recorderManager.startRecording({
96
105
  dictationConfig: this.dictationConfig,
97
- authToken: this.authToken,
106
+ serverConfig: this._serverConfig,
98
107
  });
99
108
  }
100
109
  }
101
110
  _toggleRecording() {
102
- if (!this.authToken)
111
+ if (!this._serverConfig)
103
112
  return;
104
113
  if (this._recordingState === 'recording') {
105
114
  this.recorderManager.stopRecording();
@@ -107,7 +116,7 @@ export class CortiDictation extends LitElement {
107
116
  else if (this._recordingState === 'stopped') {
108
117
  this.recorderManager.startRecording({
109
118
  dictationConfig: this.dictationConfig,
110
- authToken: this.authToken,
119
+ serverConfig: this._serverConfig,
111
120
  debug_displayAudio: this.debug_displayAudio,
112
121
  });
113
122
  }
@@ -118,40 +127,32 @@ export class CortiDictation extends LitElement {
118
127
  this.setRecordingDevice(customEvent.detail.selectedDevice);
119
128
  }
120
129
  render() {
121
- const isConfigured = this.authToken;
122
- if (!isConfigured) {
123
- return html `
124
- <div class="wrapper">
125
- <div class="callout red small">No Auth Token</div>
126
- </div>
127
- `;
128
- }
129
130
  const isLoading = this._recordingState === 'initializing' ||
130
131
  this._recordingState === 'stopping';
131
132
  const isRecording = this._recordingState === 'recording';
132
- return html `
133
- <div class="wrapper">
134
- <button
135
- @click=${this._toggleRecording}
136
- class=${isRecording ? 'red' : 'accent'}
137
- >
133
+ return html `
134
+ <div class="wrapper">
135
+ <button
136
+ @click=${this._toggleRecording}
137
+ class=${isRecording ? 'red' : 'accent'}
138
+ >
138
139
  ${isLoading
139
140
  ? html `<icon-loading-spinner></icon-loading-spinner>`
140
141
  : isRecording
141
142
  ? html `<icon-recording></icon-recording>`
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>
143
+ : html `<icon-mic-on></icon-mic-on>`}
144
+ <audio-visualiser
145
+ .level=${this._audioLevel}
146
+ .active=${isRecording}
147
+ ></audio-visualiser>
148
+ </button>
149
+
150
+ <settings-menu
151
+ .selectedDevice=${this._selectedDevice}
152
+ ?settingsDisabled=${this._recordingState !== 'stopped'}
153
+ @recording-devices-changed=${this._onRecordingDevicesChanged}
154
+ ></settings-menu>
155
+ </div>
155
156
  `;
156
157
  }
157
158
  }
@@ -159,12 +160,12 @@ CortiDictation.styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyl
159
160
  __decorate([
160
161
  property({ type: Object })
161
162
  ], CortiDictation.prototype, "dictationConfig", void 0);
162
- __decorate([
163
- property({ type: String })
164
- ], CortiDictation.prototype, "authToken", void 0);
165
163
  __decorate([
166
164
  property({ type: Boolean })
167
165
  ], CortiDictation.prototype, "debug_displayAudio", void 0);
166
+ __decorate([
167
+ state()
168
+ ], CortiDictation.prototype, "_serverConfig", void 0);
168
169
  __decorate([
169
170
  state()
170
171
  ], CortiDictation.prototype, "_audioLevel", void 0);
@@ -1 +1 @@
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"]}
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, RecordingState, ServerConfig } from './types.js';\nimport { DEFAULT_DICTATION_CONFIG } from './constants.js';\nimport CalloutStyles from './styles/callout.js';\nimport { decodeToken } from './utils.js';\n\nexport class CortiDictation extends LitElement {\n static styles = [ButtonStyles, ThemeStyles, ComponentStyles, CalloutStyles];\n\n @property({ type: Object })\n dictationConfig: DictationConfig = DEFAULT_DICTATION_CONFIG;\n\n @property({ type: Boolean })\n debug_displayAudio: boolean = false;\n\n @state()\n private _serverConfig: ServerConfig | undefined;\n\n @state()\n private _audioLevel: number = 0;\n\n @state()\n private _recordingState: RecordingState = 'stopped';\n\n @state()\n private _selectedDevice: MediaDeviceInfo | undefined;\n\n @state()\n private _devices: MediaDeviceInfo[] = [];\n\n private recorderManager = new RecorderManager();\n\n async connectedCallback() {\n super.connectedCallback();\n const devices = await this.recorderManager.initialize();\n if (devices.selectedDevice) {\n this._selectedDevice = this.recorderManager.selectedDevice;\n this._devices = this.recorderManager.devices;\n this.dispatchEvent(new CustomEvent('ready'));\n }\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 'devices-changed': () => {\n this._devices = [...this.recorderManager.devices];\n this.requestUpdate();\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 'recording-devices-changed',\n 'audio-level-changed',\n 'error',\n 'transcript',\n 'command',\n 'ready',\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 public setAccessToken(token: string) {\n const decoded = decodeToken(token);\n if (!decoded) {\n throw new Error('Invalid token');\n }\n this._serverConfig = decoded;\n return decoded;\n }\n\n public get selectedDevice(): MediaDeviceInfo | null {\n return this.recorderManager.selectedDevice || null;\n }\n\n public get recordingState(): RecordingState {\n return this._recordingState;\n }\n\n public get devices(): MediaDeviceInfo[] {\n return this._devices;\n }\n\n public async setRecordingDevice(device: MediaDeviceInfo) {\n this.recorderManager.selectedDevice = device;\n this._selectedDevice = device;\n if (!this._serverConfig) return;\n if (this._recordingState === 'recording') {\n await this.recorderManager.stopRecording();\n await this.recorderManager.startRecording({\n dictationConfig: this.dictationConfig,\n serverConfig: this._serverConfig,\n });\n }\n }\n\n _toggleRecording() {\n if (!this._serverConfig) 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 serverConfig: this._serverConfig,\n debug_displayAudio: this.debug_displayAudio,\n });\n }\n }\n\n // Handle device change events if needed\n async _onRecordingDevicesChanged(event: Event) {\n const customEvent = event as CustomEvent;\n this.setRecordingDevice(customEvent.detail.selectedDevice);\n }\n\n render() {\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 .selectedDevice=${this._selectedDevice}\n ?settingsDisabled=${this._recordingState !== 'stopped'}\n @recording-devices-changed=${this._onRecordingDevicesChanged}\n ></settings-menu>\n </div>\n `;\n }\n}\n\nexport default CortiDictation;\n"]}
@@ -1,12 +1,12 @@
1
- import type { DictationConfig } from './types.js';
1
+ import type { DictationConfig, ServerConfig } from './types.js';
2
2
  export declare class DictationService extends EventTarget {
3
3
  private mediaRecorder;
4
4
  private webSocket;
5
- private authToken;
5
+ private serverConfig;
6
6
  private dictationConfig;
7
- constructor(mediaStream: MediaStream, { dictationConfig, authToken, }: {
7
+ constructor(mediaStream: MediaStream, { dictationConfig, serverConfig, }: {
8
8
  dictationConfig: DictationConfig;
9
- authToken: string;
9
+ serverConfig: ServerConfig;
10
10
  });
11
11
  private dispatchCustomEvent;
12
12
  startRecording(): void;
@@ -1,15 +1,9 @@
1
- import { decodeToken } from './utils.js';
2
1
  export class DictationService extends EventTarget {
3
- constructor(mediaStream, { dictationConfig, authToken, }) {
2
+ constructor(mediaStream, { dictationConfig, serverConfig, }) {
4
3
  super();
5
4
  this.mediaRecorder = new MediaRecorder(mediaStream);
6
- this.authToken = authToken;
5
+ this.serverConfig = serverConfig;
7
6
  this.dictationConfig = dictationConfig;
8
- // Decode token during construction.
9
- const config = decodeToken(this.authToken);
10
- if (!config) {
11
- throw new Error('Invalid token');
12
- }
13
7
  this.mediaRecorder.ondataavailable = event => {
14
8
  if (this.webSocket?.readyState === WebSocket.OPEN) {
15
9
  this.webSocket.send(event.data);
@@ -24,8 +18,7 @@ export class DictationService extends EventTarget {
24
18
  }));
25
19
  }
26
20
  startRecording() {
27
- const serverConfig = decodeToken(this.authToken);
28
- if (!serverConfig) {
21
+ if (!this.serverConfig) {
29
22
  this.dispatchEvent(new CustomEvent('error', {
30
23
  detail: 'Invalid token',
31
24
  bubbles: true,
@@ -33,7 +26,7 @@ export class DictationService extends EventTarget {
33
26
  }));
34
27
  return;
35
28
  }
36
- const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;
29
+ const url = `wss://api.${this.serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${this.serverConfig.tenant}&token=Bearer%20${this.serverConfig.accessToken}`;
37
30
  this.webSocket = new WebSocket(url);
38
31
  this.webSocket.onopen = () => {
39
32
  this.webSocket.send(JSON.stringify({
@@ -1 +1 @@
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
+ {"version":3,"file":"DictationService.js","sourceRoot":"","sources":["../src/DictationService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DictationConfig, ServerConfig } from './types.js';\n\nexport class DictationService extends EventTarget {\n private mediaRecorder: MediaRecorder;\n private webSocket!: WebSocket;\n private serverConfig: ServerConfig;\n private dictationConfig: DictationConfig;\n\n constructor(\n mediaStream: MediaStream,\n {\n dictationConfig,\n serverConfig,\n }: { dictationConfig: DictationConfig; serverConfig: ServerConfig },\n ) {\n super();\n this.mediaRecorder = new MediaRecorder(mediaStream);\n this.serverConfig = serverConfig;\n this.dictationConfig = dictationConfig;\n\n this.mediaRecorder.ondataavailable = event => {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(event.data);\n }\n };\n }\n\n private dispatchCustomEvent(eventName: string, detail?: unknown): void {\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n public startRecording() {\n if (!this.serverConfig) {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: 'Invalid token',\n bubbles: true,\n composed: true,\n }),\n );\n return;\n }\n\n const url = `wss://api.${this.serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${this.serverConfig.tenant}&token=Bearer%20${this.serverConfig.accessToken}`;\n this.webSocket = new WebSocket(url);\n\n this.webSocket.onopen = () => {\n this.webSocket.send(\n JSON.stringify({\n type: 'config',\n configuration: this.dictationConfig,\n }),\n );\n };\n\n this.webSocket.onmessage = event => {\n const message = JSON.parse(event.data);\n switch (message.type) {\n case 'CONFIG_ACCEPTED':\n this.mediaRecorder.start(250);\n break;\n case 'CONFIG_DENIED':\n this.dispatchCustomEvent('error', message);\n return this.stopRecording();\n case 'transcript':\n this.dispatchCustomEvent('transcript', message);\n break;\n case 'command':\n this.dispatchCustomEvent('command', message);\n break;\n default:\n console.warn(`Unhandled message type: ${message.type}`);\n break;\n }\n };\n\n this.webSocket.onerror = event => {\n this.dispatchCustomEvent('error', event);\n };\n\n this.webSocket.onclose = event => {\n this.dispatchCustomEvent('stream-closed', event);\n };\n }\n\n public async stopRecording(): Promise<void> {\n this.mediaRecorder?.stop();\n\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(JSON.stringify({ type: 'end' }));\n }\n\n const timeout: NodeJS.Timeout = setTimeout(() => {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.close();\n }\n }, 10000);\n\n this.webSocket.onclose = () => {\n this.webSocket?.close();\n clearTimeout(timeout);\n };\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { DictationConfig, RecordingState } from './types.js';
1
+ import type { DictationConfig, RecordingState, ServerConfig } from './types.js';
2
2
  export declare class RecorderManager extends EventTarget {
3
3
  devices: MediaDeviceInfo[];
4
4
  selectedDevice: MediaDeviceInfo | undefined;
@@ -16,7 +16,7 @@ export declare class RecorderManager extends EventTarget {
16
16
  private handleDevicesChange;
17
17
  startRecording(params: {
18
18
  dictationConfig: DictationConfig;
19
- authToken: string;
19
+ serverConfig: ServerConfig;
20
20
  debug_displayAudio?: boolean;
21
21
  }): Promise<void>;
22
22
  stopRecording(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAudioDevices, getMediaStream } from './utils.js';\r\nimport { AudioService } from './audioService.js';\r\nimport { DictationService } from './DictationService.js';\r\nimport type { DictationConfig, RecordingState } from './types.js';\r\n\r\nexport class RecorderManager extends EventTarget {\r\n public devices: MediaDeviceInfo[] = [];\r\n\r\n public selectedDevice: MediaDeviceInfo | undefined;\r\n\r\n public recordingState: RecordingState = 'stopped';\r\n\r\n private _mediaStream: MediaStream | null = null;\r\n\r\n private _audioService: AudioService | null = null;\r\n\r\n private _dictationService: DictationService | null = null;\r\n\r\n private _visualiserInterval?: number;\r\n\r\n constructor() {\r\n super();\r\n navigator.mediaDevices.addEventListener(\r\n 'devicechange',\r\n this.handleDevicesChange.bind(this),\r\n );\r\n }\r\n\r\n async initialize() {\r\n const deviceResponse = await getAudioDevices();\r\n this.devices = deviceResponse.devices;\r\n this.selectedDevice = deviceResponse.defaultDevice;\r\n return { devices: this.devices, selectedDevice: this.selectedDevice };\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 private async handleDevicesChange() {\r\n const deviceResponse = await getAudioDevices();\r\n this.devices = deviceResponse.devices;\r\n if (\r\n !this.devices.find(\r\n device => device.deviceId === this.selectedDevice?.deviceId,\r\n )\r\n ) {\r\n this.selectedDevice = deviceResponse.defaultDevice;\r\n }\r\n this.dispatchCustomEvent('recording-devices-changed', {\r\n devices: deviceResponse.devices,\r\n selectedDevice: this.selectedDevice || deviceResponse.defaultDevice,\r\n });\r\n }\r\n\r\n async startRecording(params: {\r\n dictationConfig: DictationConfig;\r\n authToken: string;\r\n debug_displayAudio?: boolean;\r\n }): Promise<void> {\r\n this._updateRecordingState('initializing');\r\n\r\n try {\r\n this._mediaStream = await getMediaStream(\r\n params.debug_displayAudio\r\n ? 'display_audio'\r\n : this.selectedDevice?.deviceId,\r\n );\r\n\r\n this._mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\r\n track.addEventListener('ended', () => {\r\n if (this.recordingState === 'recording') {\r\n this.dispatchCustomEvent('error', {\r\n message: 'Microphone access was lost.',\r\n });\r\n this.stopRecording();\r\n }\r\n });\r\n });\r\n\r\n this._audioService = new AudioService(this._mediaStream);\r\n } catch (error) {\r\n this.dispatchCustomEvent('error', error);\r\n this.stopRecording();\r\n return;\r\n }\r\n\r\n // Initialize dictation service.\r\n try {\r\n this._dictationService = new DictationService(this._mediaStream, params);\r\n\r\n // Forward custom events from dictation service\r\n this._dictationService.addEventListener('error', e => {\r\n this.dispatchEvent(\r\n new CustomEvent('error', {\r\n detail: (e as CustomEvent).detail,\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n this.stopRecording();\r\n });\r\n this._dictationService.addEventListener('stream-closed', () =>\r\n this.stopRecording(),\r\n );\r\n this._dictationService.addEventListener('transcript', e =>\r\n this.dispatchEvent(\r\n new CustomEvent('transcript', {\r\n detail: (e as CustomEvent).detail,\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n ),\r\n );\r\n this._dictationService.addEventListener('command', e =>\r\n this.dispatchEvent(\r\n new CustomEvent('command', {\r\n detail: (e as CustomEvent).detail,\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n ),\r\n );\r\n } catch (error) {\r\n this.dispatchCustomEvent('error', error);\r\n this.stopRecording();\r\n return;\r\n }\r\n\r\n try {\r\n this._dictationService?.startRecording();\r\n } catch (error) {\r\n this.dispatchEvent(\r\n new CustomEvent('error', {\r\n detail: error,\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n this.stopRecording();\r\n return;\r\n }\r\n this._updateRecordingState('recording');\r\n\r\n // Update audio level visualization.\r\n this._visualiserInterval = window.setInterval(() => {\r\n const level = this._audioService\r\n ? this._audioService.getAudioLevel() * 3\r\n : 0;\r\n this.dispatchCustomEvent('audio-level-changed', { audioLevel: level });\r\n }, 150);\r\n }\r\n\r\n async stopRecording() {\r\n this._updateRecordingState('stopping');\r\n if (this._visualiserInterval) {\r\n clearInterval(this._visualiserInterval);\r\n this._visualiserInterval = undefined;\r\n }\r\n if (this._mediaStream) {\r\n this._mediaStream.getTracks().forEach(track => track.stop());\r\n }\r\n this.dispatchCustomEvent('audio-level-changed', { audioLevel: 0 });\r\n await this._dictationService?.stopRecording();\r\n this._updateRecordingState('stopped');\r\n }\r\n\r\n private _updateRecordingState(state: RecordingState) {\r\n this.recordingState = state;\r\n this.dispatchEvent(\r\n new CustomEvent('recording-state-changed', {\r\n detail: { state },\r\n bubbles: true,\r\n composed: true,\r\n }),\r\n );\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAudioDevices, getMediaStream } from './utils.js';\nimport { AudioService } from './audioService.js';\nimport { DictationService } from './DictationService.js';\nimport type { DictationConfig, RecordingState, ServerConfig } from './types.js';\n\nexport class RecorderManager extends EventTarget {\n public devices: MediaDeviceInfo[] = [];\n\n public selectedDevice: MediaDeviceInfo | undefined;\n\n public recordingState: RecordingState = 'stopped';\n\n private _mediaStream: MediaStream | null = null;\n\n private _audioService: AudioService | null = null;\n\n private _dictationService: DictationService | null = null;\n\n private _visualiserInterval?: number;\n\n constructor() {\n super();\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n this.handleDevicesChange.bind(this),\n );\n }\n\n async initialize() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n this.selectedDevice = deviceResponse.defaultDevice;\n return { devices: this.devices, selectedDevice: this.selectedDevice };\n }\n\n private dispatchCustomEvent(eventName: string, detail?: unknown): void {\n this.dispatchEvent(\n new CustomEvent(eventName, {\n detail,\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private async handleDevicesChange() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n if (\n !this.devices.find(\n device => device.deviceId === this.selectedDevice?.deviceId,\n )\n ) {\n this.selectedDevice = deviceResponse.defaultDevice;\n }\n this.dispatchCustomEvent('recording-devices-changed', {\n devices: deviceResponse.devices,\n selectedDevice: this.selectedDevice || deviceResponse.defaultDevice,\n });\n }\n\n async startRecording(params: {\n dictationConfig: DictationConfig;\n serverConfig: ServerConfig;\n debug_displayAudio?: boolean;\n }): Promise<void> {\n this._updateRecordingState('initializing');\n\n try {\n this._mediaStream = await getMediaStream(\n params.debug_displayAudio\n ? 'display_audio'\n : this.selectedDevice?.deviceId,\n );\n\n this._mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.addEventListener('ended', () => {\n if (this.recordingState === 'recording') {\n this.dispatchCustomEvent('error', {\n message: 'Microphone access was lost.',\n });\n this.stopRecording();\n }\n });\n });\n\n this._audioService = new AudioService(this._mediaStream);\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this.stopRecording();\n return;\n }\n\n // Initialize dictation service.\n try {\n this._dictationService = new DictationService(this._mediaStream, params);\n\n // Forward custom events from dictation service\n this._dictationService.addEventListener('error', e => {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n );\n this.stopRecording();\n });\n this._dictationService.addEventListener('stream-closed', () =>\n this.stopRecording(),\n );\n this._dictationService.addEventListener('transcript', e =>\n this.dispatchEvent(\n new CustomEvent('transcript', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n this._dictationService.addEventListener('command', e =>\n this.dispatchEvent(\n new CustomEvent('command', {\n detail: (e as CustomEvent).detail,\n bubbles: true,\n composed: true,\n }),\n ),\n );\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this.stopRecording();\n return;\n }\n\n try {\n this._dictationService?.startRecording();\n } catch (error) {\n this.dispatchEvent(\n new CustomEvent('error', {\n detail: error,\n bubbles: true,\n composed: true,\n }),\n );\n this.stopRecording();\n return;\n }\n this._updateRecordingState('recording');\n\n // Update audio level visualization.\n this._visualiserInterval = window.setInterval(() => {\n const level = this._audioService\n ? this._audioService.getAudioLevel() * 3\n : 0;\n this.dispatchCustomEvent('audio-level-changed', { audioLevel: level });\n }, 150);\n }\n\n async stopRecording() {\n this._updateRecordingState('stopping');\n if (this._visualiserInterval) {\n clearInterval(this._visualiserInterval);\n this._visualiserInterval = undefined;\n }\n if (this._mediaStream) {\n this._mediaStream.getTracks().forEach(track => track.stop());\n }\n this.dispatchCustomEvent('audio-level-changed', { audioLevel: 0 });\n await this._dictationService?.stopRecording();\n this._updateRecordingState('stopped');\n }\n\n private _updateRecordingState(state: RecordingState) {\n this.recordingState = state;\n this.dispatchEvent(\n new CustomEvent('recording-state-changed', {\n detail: { state },\n bubbles: true,\n composed: true,\n }),\n );\n }\n}\n"]}
package/dist/bundle.js CHANGED
@@ -659,7 +659,7 @@ function decodeToken(token) {
659
659
  return {
660
660
  environment: match[2],
661
661
  tenant: match[3],
662
- token
662
+ accessToken: token
663
663
  };
664
664
  }
665
665
  }
@@ -707,15 +707,11 @@ var AudioService = class {
707
707
 
708
708
  // dist/DictationService.js
709
709
  var DictationService = class extends EventTarget {
710
- constructor(mediaStream, { dictationConfig, authToken }) {
710
+ constructor(mediaStream, { dictationConfig, serverConfig }) {
711
711
  super();
712
712
  this.mediaRecorder = new MediaRecorder(mediaStream);
713
- this.authToken = authToken;
713
+ this.serverConfig = serverConfig;
714
714
  this.dictationConfig = dictationConfig;
715
- const config = decodeToken(this.authToken);
716
- if (!config) {
717
- throw new Error("Invalid token");
718
- }
719
715
  this.mediaRecorder.ondataavailable = (event) => {
720
716
  if (this.webSocket?.readyState === WebSocket.OPEN) {
721
717
  this.webSocket.send(event.data);
@@ -730,8 +726,7 @@ var DictationService = class extends EventTarget {
730
726
  }));
731
727
  }
732
728
  startRecording() {
733
- const serverConfig = decodeToken(this.authToken);
734
- if (!serverConfig) {
729
+ if (!this.serverConfig) {
735
730
  this.dispatchEvent(new CustomEvent("error", {
736
731
  detail: "Invalid token",
737
732
  bubbles: true,
@@ -739,7 +734,7 @@ var DictationService = class extends EventTarget {
739
734
  }));
740
735
  return;
741
736
  }
742
- const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;
737
+ const url = `wss://api.${this.serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${this.serverConfig.tenant}&token=Bearer%20${this.serverConfig.accessToken}`;
743
738
  this.webSocket = new WebSocket(url);
744
739
  this.webSocket.onopen = () => {
745
740
  this.webSocket.send(JSON.stringify({
@@ -1019,9 +1014,9 @@ var DEFAULT_DICTATION_CONFIG = {
1019
1014
  // dist/styles/callout.js
1020
1015
  var CalloutStyles = i`
1021
1016
  .callout {
1022
- background: var(--callout-accent-background);
1023
- border: 1px solid var(--callout-accent-border);
1024
- color: var(--callout-accent-text);
1017
+ background: var(--callout-info-background);
1018
+ border: 1px solid var(--callout-info-border);
1019
+ color: var(--callout-info-text);
1025
1020
  padding: 8px;
1026
1021
  border-radius: var(--card-inner-border-radius);
1027
1022
  display: flex;
@@ -1030,15 +1025,15 @@ var CalloutStyles = i`
1030
1025
  align-items: center;
1031
1026
  max-width: 100%;
1032
1027
  height: fit-content;
1033
- &.red {
1034
- background: var(--callout-red-background);
1035
- border: 1px solid var(--callout-red-border);
1036
- color: var(--callout-red-text);
1028
+ &.error {
1029
+ background: var(--callout-error-background);
1030
+ border: 1px solid var(--callout-error-border);
1031
+ color: var(--callout-warn-text);
1037
1032
  }
1038
- &.orange {
1039
- background: var(--callout-orange-background);
1040
- border: 1px solid var(--callout-orange-border);
1041
- color: var(--callout-orange-text);
1033
+ &.warn {
1034
+ background: var(--callout-warn-background);
1035
+ border: 1px solid var(--callout-warn-border);
1036
+ color: var(--callout-warn-text);
1042
1037
  }
1043
1038
  &.small {
1044
1039
  width: 100%;
@@ -1098,7 +1093,7 @@ var SettingsMenu = class SettingsMenu2 extends r4 {
1098
1093
  <div id="settings-popover" popover>
1099
1094
  <div class="settings-wrapper">
1100
1095
  ${this.settingsDisabled ? x`
1101
- <div class="callout orange">
1096
+ <div class="callout warn">
1102
1097
  Recording is in progress. Stop recording to change settings.
1103
1098
  </div>
1104
1099
  ` : ""}
@@ -1175,10 +1170,9 @@ SettingsMenu.styles = [
1175
1170
  width: 100%;
1176
1171
  min-width: 200px;
1177
1172
  position-anchor: --settings_popover_btn;
1178
- position-area: bottom;
1173
+ position-area: bottom span-right;
1179
1174
  position-visibility: always;
1180
- /* inset: unset; */
1181
- transform: translateX(40%);
1175
+ position-try-fallbacks: flip-inline;
1182
1176
  overflow-x: hidden;
1183
1177
  }
1184
1178
  .settings-wrapper {
@@ -1423,69 +1417,48 @@ IconLoadingSpinner = __decorate3([
1423
1417
  // dist/styles/theme.js
1424
1418
  var ThemeStyles = i`
1425
1419
  :host {
1420
+ color-scheme: light dark;
1426
1421
  /* Component Defaults */
1427
1422
  --component-font-family: 'Segoe UI', Roboto, sans-serif;
1428
- --component-text-color: #333;
1423
+ --component-text-color: light-dark(#333, #eee);
1429
1424
 
1430
1425
  /* Card Defaults */
1431
- --card-background: #fff;
1432
- --card-border-color: #ddd;
1426
+ --card-background: light-dark(#fff, #333);
1427
+ --card-border-color: light-dark(#ddd, #555);
1433
1428
  --card-padding: 4px;
1434
1429
  --card-border-radius: 8px;
1435
1430
  --card-inner-border-radius: 6px;
1436
1431
  --card-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
1437
1432
 
1438
1433
  /* Actions Defaults */
1439
- --action-plain-border-color: #ccc;
1440
- --action-plain-background-hover: #ddd;
1434
+ --action-plain-border-color: light-dark(#ccc, #555);
1435
+ --action-plain-background-hover: light-dark(#ddd, #444);
1441
1436
 
1442
- --action-accent-background: #007bff;
1443
- --action-accent-background-hover: #0056b3;
1437
+ --action-accent-background: light-dark(#007bff, #0056b3);
1438
+ --action-accent-background-hover: light-dark(#0056b3, #003d80);
1444
1439
  --action-accent-text-color: #fff;
1445
1440
 
1446
- --action-red-background: #dc3545;
1447
- --action-red-background-hover: #bd2130;
1441
+ --action-red-background: light-dark(#dc3545, #bd2130);
1442
+ --action-red-background-hover: light-dark(#bd2130, #a71c24);
1448
1443
  --action-red-text-color: #fff;
1449
1444
 
1450
1445
  /* Callout Defaults */
1451
- --callout-accent-background: #007bff33;
1452
- --callout-accent-border: #007bff99;
1453
- --callout-accent-text: #007bff;
1446
+ --callout-info-background: light-dark(#007bff33, #0056b333);
1447
+ --callout-info-border: light-dark(#007bff99, #0056b399);
1448
+ --callout-info-text: light-dark(#007bff, #0056b3);
1454
1449
 
1455
- --callout-red-background: #dc354533;
1456
- --callout-red-border: #dc354599;
1457
- --callout-red-text: #dc3545;
1450
+ --callout-error-background: light-dark(#dc354533, #bd213033);
1451
+ --callout-error-border: light-dark(#dc354599, #bd213099);
1452
+ --callout-error-text: light-dark(#dc3545, #bd2130);
1458
1453
 
1459
- --callout-orange-background: #fd7e1433;
1460
- --callout-orange-border: #fd7e1499;
1461
- --callout-orange-text: #fd7e14;
1454
+ --callout-warn-background: light-dark(#fd7e1433, #e06c1233);
1455
+ --callout-warn-border: light-dark(#fd7e1499, #e06c1299);
1456
+ --callout-warn-text: light-dark(#fd7e14, #e06c12);
1462
1457
 
1463
1458
  /* Visualiser Defaults */
1464
- --visualiser-background: #e0e0e0;
1465
- --visualiser-level-color: #28a745;
1459
+ --visualiser-background: light-dark(#e0e0e0, #fff);
1466
1460
  }
1467
1461
 
1468
- @media (prefers-color-scheme: dark) {
1469
- :host {
1470
- /* Component Dark */
1471
- --component-text-color: #eee;
1472
-
1473
- /* Card Dark */
1474
- --card-background: #333;
1475
- --card-border-color: #555;
1476
-
1477
- /* Actions Dark */
1478
- --action-plain-border-color: #555;
1479
- --action-plain-background: #333;
1480
- --action-plain-background-hover: #444;
1481
-
1482
- --action-accent-background: #0056b3;
1483
- --action-accent-background-hover: #003d80;
1484
-
1485
- /* Visualiser Dark */
1486
- --visualiser-background: #fff;
1487
- }
1488
- }
1489
1462
  :host {
1490
1463
  box-sizing: border-box;
1491
1464
  font-family: var(--component-font-family);
@@ -1611,6 +1584,14 @@ var CortiDictation = class extends r4 {
1611
1584
  toggleRecording() {
1612
1585
  this._toggleRecording();
1613
1586
  }
1587
+ setAccessToken(token) {
1588
+ const decoded = decodeToken(token);
1589
+ if (!decoded) {
1590
+ throw new Error("Invalid token");
1591
+ }
1592
+ this._serverConfig = decoded;
1593
+ return decoded;
1594
+ }
1614
1595
  get selectedDevice() {
1615
1596
  return this.recorderManager.selectedDevice || null;
1616
1597
  }
@@ -1623,25 +1604,25 @@ var CortiDictation = class extends r4 {
1623
1604
  async setRecordingDevice(device) {
1624
1605
  this.recorderManager.selectedDevice = device;
1625
1606
  this._selectedDevice = device;
1626
- if (!this.authToken)
1607
+ if (!this._serverConfig)
1627
1608
  return;
1628
1609
  if (this._recordingState === "recording") {
1629
1610
  await this.recorderManager.stopRecording();
1630
1611
  await this.recorderManager.startRecording({
1631
1612
  dictationConfig: this.dictationConfig,
1632
- authToken: this.authToken
1613
+ serverConfig: this._serverConfig
1633
1614
  });
1634
1615
  }
1635
1616
  }
1636
1617
  _toggleRecording() {
1637
- if (!this.authToken)
1618
+ if (!this._serverConfig)
1638
1619
  return;
1639
1620
  if (this._recordingState === "recording") {
1640
1621
  this.recorderManager.stopRecording();
1641
1622
  } else if (this._recordingState === "stopped") {
1642
1623
  this.recorderManager.startRecording({
1643
1624
  dictationConfig: this.dictationConfig,
1644
- authToken: this.authToken,
1625
+ serverConfig: this._serverConfig,
1645
1626
  debug_displayAudio: this.debug_displayAudio
1646
1627
  });
1647
1628
  }
@@ -1652,14 +1633,6 @@ var CortiDictation = class extends r4 {
1652
1633
  this.setRecordingDevice(customEvent.detail.selectedDevice);
1653
1634
  }
1654
1635
  render() {
1655
- const isConfigured = this.authToken;
1656
- if (!isConfigured) {
1657
- return x`
1658
- <div class="wrapper">
1659
- <div class="callout red small">No Auth Token</div>
1660
- </div>
1661
- `;
1662
- }
1663
1636
  const isLoading = this._recordingState === "initializing" || this._recordingState === "stopping";
1664
1637
  const isRecording = this._recordingState === "recording";
1665
1638
  return x`
@@ -1688,12 +1661,12 @@ CortiDictation.styles = [buttons_default, theme_default, ComponentStyles_default
1688
1661
  __decorate4([
1689
1662
  n4({ type: Object })
1690
1663
  ], CortiDictation.prototype, "dictationConfig", void 0);
1691
- __decorate4([
1692
- n4({ type: String })
1693
- ], CortiDictation.prototype, "authToken", void 0);
1694
1664
  __decorate4([
1695
1665
  n4({ type: Boolean })
1696
1666
  ], CortiDictation.prototype, "debug_displayAudio", void 0);
1667
+ __decorate4([
1668
+ r6()
1669
+ ], CortiDictation.prototype, "_serverConfig", void 0);
1697
1670
  __decorate4([
1698
1671
  r6()
1699
1672
  ], CortiDictation.prototype, "_audioLevel", void 0);
@@ -56,7 +56,7 @@ let SettingsMenu = class SettingsMenu extends LitElement {
56
56
  <div class="settings-wrapper">
57
57
  ${this.settingsDisabled
58
58
  ? html `
59
- <div class="callout orange">
59
+ <div class="callout warn">
60
60
  Recording is in progress. Stop recording to change settings.
61
61
  </div>
62
62
  `
@@ -134,10 +134,9 @@ SettingsMenu.styles = [
134
134
  width: 100%;
135
135
  min-width: 200px;
136
136
  position-anchor: --settings_popover_btn;
137
- position-area: bottom;
137
+ position-area: bottom span-right;
138
138
  position-visibility: always;
139
- /* inset: unset; */
140
- transform: translateX(40%);
139
+ position-try-fallbacks: flip-inline;
141
140
  overflow-x: hidden;
142
141
  }
143
142
  .settings-wrapper {
@@ -1 +1 @@
1
- {"version":3,"file":"settings-menu.js","sourceRoot":"","sources":["../../src/components/settings-menu.ts"],"names":[],"mappings":"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","sourcesContent":["// mic-selector.ts\nimport { LitElement, html, css, TemplateResult, CSSResultGroup } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport ButtonStyles from '../styles/buttons.js';\nimport SelectStyles from '../styles/select.js';\nimport { LANGUAGES_SUPPORTED } from '../constants.js';\nimport { getAudioDevices, getLanguageName } from '../utils.js';\nimport CalloutStyles from '../styles/callout.js';\n\n@customElement('settings-menu')\nexport class SettingsMenu extends LitElement {\n @property({ type: String })\n selectedDevice: MediaDeviceInfo | undefined;\n\n @property({ type: String })\n selectedLanguage: string = '';\n\n @property({ type: Boolean })\n settingsDisabled: boolean = false;\n\n @state()\n private _devices: MediaDeviceInfo[] = [];\n\n constructor() {\n super();\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n this.handleDevicesChange.bind(this),\n );\n }\n\n // on load, get the available devices\n async connectedCallback(): Promise<void> {\n super.connectedCallback();\n const deviceResponse = await getAudioDevices();\n this._devices = deviceResponse.devices;\n }\n\n private async handleDevicesChange() {\n const deviceResponse = await getAudioDevices();\n this._devices = deviceResponse.devices;\n }\n\n static styles: CSSResultGroup = [\n css`\n :host {\n display: block;\n font-family: var(--component-font-family);\n }\n /* Retain the anchor-name styling for this component */\n #settings-popover-button {\n anchor-name: --settings_popover_btn;\n }\n [popover] {\n margin: 0;\n padding: 16px;\n border: 0;\n background: var(--card-background);\n border: 1px solid var(--card-border-color);\n border-radius: var(--card-border-radius);\n box-shadow: var(--card-box-shadow);\n z-index: 1000;\n max-width: 260px;\n width: 100%;\n min-width: 200px;\n position-anchor: --settings_popover_btn;\n position-area: bottom;\n position-visibility: always;\n /* inset: unset; */\n transform: translateX(40%);\n overflow-x: hidden;\n }\n .settings-wrapper {\n display: flex;\n flex-direction: column;\n gap: 20px;\n }\n `,\n ButtonStyles,\n SelectStyles,\n CalloutStyles,\n ];\n\n private _selectDevice(deviceId: string): void {\n // Find the device object\n const device = this._devices.find(d => d.deviceId === deviceId);\n if (!device) {\n return;\n }\n this.selectedDevice = device;\n this.dispatchEvent(\n new CustomEvent('recording-devices-changed', {\n detail: {\n devices: this._devices,\n selectedDevice: device,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n render(): TemplateResult {\n return html`\n <div class=\"mic-selector\">\n <button id=\"settings-popover-button\" popovertarget=\"settings-popover\">\n <icon-settings></icon-settings>\n </button>\n <div id=\"settings-popover\" popover>\n <div class=\"settings-wrapper\">\n ${this.settingsDisabled\n ? html`\n <div class=\"callout orange\">\n Recording is in progress. Stop recording to change settings.\n </div>\n `\n : ''}\n <div class=\"form-group\">\n <label id=\"device-select-label\" for=\"device-select\">\n Recording Device\n </label>\n <select\n id=\"device-select\"\n aria-labelledby=\"device-select-label\"\n @change=${(e: Event) => {\n this._selectDevice((e.target as HTMLSelectElement).value);\n }}\n ?disabled=${this.settingsDisabled}\n >\n ${this._devices.map(\n device => html`\n <option\n value=${device.deviceId}\n ?selected=${this.selectedDevice === device}\n >\n ${device.label || 'Unknown Device'}\n </option>\n `,\n )}\n </select>\n </div>\n <div class=\"form-group\">\n <label id=\"language-select-label\" for=\"language-select\">\n Dictation Language\n </label>\n <select\n id=\"language-select\"\n aria-labelledby=\"language-select-label\"\n @change=${(e: Event) => {\n this._selectDevice((e.target as HTMLSelectElement).value);\n }}\n ?disabled=${this.settingsDisabled}\n >\n ${LANGUAGES_SUPPORTED.map(\n language => html`\n <option\n value=${language}\n ?selected=${this.selectedLanguage === language}\n >\n ${getLanguageName(language)}\n </option>\n `,\n )}\n </select>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'settings-menu': SettingsMenu;\n }\n}\n"]}
1
+ {"version":3,"file":"settings-menu.js","sourceRoot":"","sources":["../../src/components/settings-menu.ts"],"names":[],"mappings":"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","sourcesContent":["// mic-selector.ts\nimport { LitElement, html, css, TemplateResult, CSSResultGroup } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport ButtonStyles from '../styles/buttons.js';\nimport SelectStyles from '../styles/select.js';\nimport { LANGUAGES_SUPPORTED } from '../constants.js';\nimport { getAudioDevices, getLanguageName } from '../utils.js';\nimport CalloutStyles from '../styles/callout.js';\n\n@customElement('settings-menu')\nexport class SettingsMenu extends LitElement {\n @property({ type: String })\n selectedDevice: MediaDeviceInfo | undefined;\n\n @property({ type: String })\n selectedLanguage: string = '';\n\n @property({ type: Boolean })\n settingsDisabled: boolean = false;\n\n @state()\n private _devices: MediaDeviceInfo[] = [];\n\n constructor() {\n super();\n navigator.mediaDevices.addEventListener(\n 'devicechange',\n this.handleDevicesChange.bind(this),\n );\n }\n\n // on load, get the available devices\n async connectedCallback(): Promise<void> {\n super.connectedCallback();\n const deviceResponse = await getAudioDevices();\n this._devices = deviceResponse.devices;\n }\n\n private async handleDevicesChange() {\n const deviceResponse = await getAudioDevices();\n this._devices = deviceResponse.devices;\n }\n\n static styles: CSSResultGroup = [\n css`\n :host {\n display: block;\n font-family: var(--component-font-family);\n }\n /* Retain the anchor-name styling for this component */\n #settings-popover-button {\n anchor-name: --settings_popover_btn;\n }\n [popover] {\n margin: 0;\n padding: 16px;\n border: 0;\n background: var(--card-background);\n border: 1px solid var(--card-border-color);\n border-radius: var(--card-border-radius);\n box-shadow: var(--card-box-shadow);\n z-index: 1000;\n max-width: 260px;\n width: 100%;\n min-width: 200px;\n position-anchor: --settings_popover_btn;\n position-area: bottom span-right;\n position-visibility: always;\n position-try-fallbacks: flip-inline;\n overflow-x: hidden;\n }\n .settings-wrapper {\n display: flex;\n flex-direction: column;\n gap: 20px;\n }\n `,\n ButtonStyles,\n SelectStyles,\n CalloutStyles,\n ];\n\n private _selectDevice(deviceId: string): void {\n // Find the device object\n const device = this._devices.find(d => d.deviceId === deviceId);\n if (!device) {\n return;\n }\n this.selectedDevice = device;\n this.dispatchEvent(\n new CustomEvent('recording-devices-changed', {\n detail: {\n devices: this._devices,\n selectedDevice: device,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n render(): TemplateResult {\n return html`\n <div class=\"mic-selector\">\n <button id=\"settings-popover-button\" popovertarget=\"settings-popover\">\n <icon-settings></icon-settings>\n </button>\n <div id=\"settings-popover\" popover>\n <div class=\"settings-wrapper\">\n ${this.settingsDisabled\n ? html`\n <div class=\"callout warn\">\n Recording is in progress. Stop recording to change settings.\n </div>\n `\n : ''}\n <div class=\"form-group\">\n <label id=\"device-select-label\" for=\"device-select\">\n Recording Device\n </label>\n <select\n id=\"device-select\"\n aria-labelledby=\"device-select-label\"\n @change=${(e: Event) => {\n this._selectDevice((e.target as HTMLSelectElement).value);\n }}\n ?disabled=${this.settingsDisabled}\n >\n ${this._devices.map(\n device => html`\n <option\n value=${device.deviceId}\n ?selected=${this.selectedDevice === device}\n >\n ${device.label || 'Unknown Device'}\n </option>\n `,\n )}\n </select>\n </div>\n <div class=\"form-group\">\n <label id=\"language-select-label\" for=\"language-select\">\n Dictation Language\n </label>\n <select\n id=\"language-select\"\n aria-labelledby=\"language-select-label\"\n @change=${(e: Event) => {\n this._selectDevice((e.target as HTMLSelectElement).value);\n }}\n ?disabled=${this.settingsDisabled}\n >\n ${LANGUAGES_SUPPORTED.map(\n language => html`\n <option\n value=${language}\n ?selected=${this.selectedLanguage === language}\n >\n ${getLanguageName(language)}\n </option>\n `,\n )}\n </select>\n </div>\n </div>\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'settings-menu': SettingsMenu;\n }\n}\n"]}
package/dist/constants.js CHANGED
@@ -4,6 +4,6 @@ export const DEFAULT_DICTATION_CONFIG = {
4
4
  interimResults: true,
5
5
  spokenPunctuation: true,
6
6
  automaticPunctuation: true,
7
- model: 'others'
7
+ model: 'others',
8
8
  };
9
9
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAChD,MAAM,CAAC,MAAM,wBAAwB,GAAoB;IACvD,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,IAAI;IACpB,iBAAiB,EAAE,IAAI;IACvB,oBAAoB,EAAE,IAAI;IAC1B,KAAK,EAAE,QAAQ;CAChB,CAAC","sourcesContent":["import { DictationConfig } from './types.js';\r\n\r\nexport const LANGUAGES_SUPPORTED = ['en', 'da'];\r\nexport const DEFAULT_DICTATION_CONFIG: DictationConfig = {\r\n primaryLanguage: 'en',\r\n interimResults: true,\r\n spokenPunctuation: true,\r\n automaticPunctuation: true,\r\n model: 'others'\r\n};\r\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAChD,MAAM,CAAC,MAAM,wBAAwB,GAAoB;IACvD,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,IAAI;IACpB,iBAAiB,EAAE,IAAI;IACvB,oBAAoB,EAAE,IAAI;IAC1B,KAAK,EAAE,QAAQ;CAChB,CAAC","sourcesContent":["import { DictationConfig } from './types.js';\n\nexport const LANGUAGES_SUPPORTED = ['en', 'da'];\nexport const DEFAULT_DICTATION_CONFIG: DictationConfig = {\n primaryLanguage: 'en',\n interimResults: true,\n spokenPunctuation: true,\n automaticPunctuation: true,\n model: 'others',\n};\n"]}
@@ -1,33 +1,33 @@
1
1
  import { css } from 'lit';
2
- const CalloutStyles = css `
3
- .callout {
4
- background: var(--callout-accent-background);
5
- border: 1px solid var(--callout-accent-border);
6
- color: var(--callout-accent-text);
7
- padding: 8px;
8
- border-radius: var(--card-inner-border-radius);
9
- display: flex;
10
- font-size: 0.9rem;
11
- gap: 8px;
12
- align-items: center;
13
- max-width: 100%;
14
- height: fit-content;
15
- &.red {
16
- background: var(--callout-red-background);
17
- border: 1px solid var(--callout-red-border);
18
- color: var(--callout-red-text);
19
- }
20
- &.orange {
21
- background: var(--callout-orange-background);
22
- border: 1px solid var(--callout-orange-border);
23
- color: var(--callout-orange-text);
24
- }
25
- &.small {
26
- width: 100%;
27
- padding: 6px;
28
- font-size: 0.7rem;
29
- }
30
- }
2
+ const CalloutStyles = css `
3
+ .callout {
4
+ background: var(--callout-info-background);
5
+ border: 1px solid var(--callout-info-border);
6
+ color: var(--callout-info-text);
7
+ padding: 8px;
8
+ border-radius: var(--card-inner-border-radius);
9
+ display: flex;
10
+ font-size: 0.9rem;
11
+ gap: 8px;
12
+ align-items: center;
13
+ max-width: 100%;
14
+ height: fit-content;
15
+ &.error {
16
+ background: var(--callout-error-background);
17
+ border: 1px solid var(--callout-error-border);
18
+ color: var(--callout-warn-text);
19
+ }
20
+ &.warn {
21
+ background: var(--callout-warn-background);
22
+ border: 1px solid var(--callout-warn-border);
23
+ color: var(--callout-warn-text);
24
+ }
25
+ &.small {
26
+ width: 100%;
27
+ padding: 6px;
28
+ font-size: 0.7rem;
29
+ }
30
+ }
31
31
  `;
32
32
  export default CalloutStyles;
33
33
  //# sourceMappingURL=callout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"callout.js","sourceRoot":"","sources":["../../src/styles/callout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,aAAa,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BxB,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import { css } from 'lit';\r\n\r\nconst CalloutStyles = css`\r\n .callout {\r\n background: var(--callout-accent-background);\r\n border: 1px solid var(--callout-accent-border);\r\n color: var(--callout-accent-text);\r\n padding: 8px;\r\n border-radius: var(--card-inner-border-radius);\r\n display: flex;\r\n font-size: 0.9rem;\r\n gap: 8px;\r\n align-items: center;\r\n max-width: 100%;\r\n height: fit-content;\r\n &.red {\r\n background: var(--callout-red-background);\r\n border: 1px solid var(--callout-red-border);\r\n color: var(--callout-red-text);\r\n }\r\n &.orange {\r\n background: var(--callout-orange-background);\r\n border: 1px solid var(--callout-orange-border);\r\n color: var(--callout-orange-text);\r\n }\r\n &.small {\r\n width: 100%;\r\n padding: 6px;\r\n font-size: 0.7rem;\r\n }\r\n }\r\n`;\r\n\r\nexport default CalloutStyles;\r\n"]}
1
+ {"version":3,"file":"callout.js","sourceRoot":"","sources":["../../src/styles/callout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,aAAa,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BxB,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst CalloutStyles = css`\n .callout {\n background: var(--callout-info-background);\n border: 1px solid var(--callout-info-border);\n color: var(--callout-info-text);\n padding: 8px;\n border-radius: var(--card-inner-border-radius);\n display: flex;\n font-size: 0.9rem;\n gap: 8px;\n align-items: center;\n max-width: 100%;\n height: fit-content;\n &.error {\n background: var(--callout-error-background);\n border: 1px solid var(--callout-error-border);\n color: var(--callout-warn-text);\n }\n &.warn {\n background: var(--callout-warn-background);\n border: 1px solid var(--callout-warn-border);\n color: var(--callout-warn-text);\n }\n &.small {\n width: 100%;\n padding: 6px;\n font-size: 0.7rem;\n }\n }\n`;\n\nexport default CalloutStyles;\n"]}
@@ -1,69 +1,48 @@
1
1
  import { css } from 'lit';
2
2
  const ThemeStyles = css `
3
3
  :host {
4
+ color-scheme: light dark;
4
5
  /* Component Defaults */
5
6
  --component-font-family: 'Segoe UI', Roboto, sans-serif;
6
- --component-text-color: #333;
7
+ --component-text-color: light-dark(#333, #eee);
7
8
 
8
9
  /* Card Defaults */
9
- --card-background: #fff;
10
- --card-border-color: #ddd;
10
+ --card-background: light-dark(#fff, #333);
11
+ --card-border-color: light-dark(#ddd, #555);
11
12
  --card-padding: 4px;
12
13
  --card-border-radius: 8px;
13
14
  --card-inner-border-radius: 6px;
14
15
  --card-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
15
16
 
16
17
  /* Actions Defaults */
17
- --action-plain-border-color: #ccc;
18
- --action-plain-background-hover: #ddd;
18
+ --action-plain-border-color: light-dark(#ccc, #555);
19
+ --action-plain-background-hover: light-dark(#ddd, #444);
19
20
 
20
- --action-accent-background: #007bff;
21
- --action-accent-background-hover: #0056b3;
21
+ --action-accent-background: light-dark(#007bff, #0056b3);
22
+ --action-accent-background-hover: light-dark(#0056b3, #003d80);
22
23
  --action-accent-text-color: #fff;
23
24
 
24
- --action-red-background: #dc3545;
25
- --action-red-background-hover: #bd2130;
25
+ --action-red-background: light-dark(#dc3545, #bd2130);
26
+ --action-red-background-hover: light-dark(#bd2130, #a71c24);
26
27
  --action-red-text-color: #fff;
27
28
 
28
29
  /* Callout Defaults */
29
- --callout-accent-background: #007bff33;
30
- --callout-accent-border: #007bff99;
31
- --callout-accent-text: #007bff;
30
+ --callout-info-background: light-dark(#007bff33, #0056b333);
31
+ --callout-info-border: light-dark(#007bff99, #0056b399);
32
+ --callout-info-text: light-dark(#007bff, #0056b3);
32
33
 
33
- --callout-red-background: #dc354533;
34
- --callout-red-border: #dc354599;
35
- --callout-red-text: #dc3545;
34
+ --callout-error-background: light-dark(#dc354533, #bd213033);
35
+ --callout-error-border: light-dark(#dc354599, #bd213099);
36
+ --callout-error-text: light-dark(#dc3545, #bd2130);
36
37
 
37
- --callout-orange-background: #fd7e1433;
38
- --callout-orange-border: #fd7e1499;
39
- --callout-orange-text: #fd7e14;
38
+ --callout-warn-background: light-dark(#fd7e1433, #e06c1233);
39
+ --callout-warn-border: light-dark(#fd7e1499, #e06c1299);
40
+ --callout-warn-text: light-dark(#fd7e14, #e06c12);
40
41
 
41
42
  /* Visualiser Defaults */
42
- --visualiser-background: #e0e0e0;
43
- --visualiser-level-color: #28a745;
43
+ --visualiser-background: light-dark(#e0e0e0, #fff);
44
44
  }
45
45
 
46
- @media (prefers-color-scheme: dark) {
47
- :host {
48
- /* Component Dark */
49
- --component-text-color: #eee;
50
-
51
- /* Card Dark */
52
- --card-background: #333;
53
- --card-border-color: #555;
54
-
55
- /* Actions Dark */
56
- --action-plain-border-color: #555;
57
- --action-plain-background: #333;
58
- --action-plain-background-hover: #444;
59
-
60
- --action-accent-background: #0056b3;
61
- --action-accent-background-hover: #003d80;
62
-
63
- /* Visualiser Dark */
64
- --visualiser-background: #fff;
65
- }
66
- }
67
46
  :host {
68
47
  box-sizing: border-box;
69
48
  font-family: var(--component-font-family);
@@ -1 +1 @@
1
- {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/styles/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,WAAW,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEtB,CAAC;AAEF,eAAe,WAAW,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst ThemeStyles = css`\n :host {\n /* Component Defaults */\n --component-font-family: 'Segoe UI', Roboto, sans-serif;\n --component-text-color: #333;\n\n /* Card Defaults */\n --card-background: #fff;\n --card-border-color: #ddd;\n --card-padding: 4px;\n --card-border-radius: 8px;\n --card-inner-border-radius: 6px;\n --card-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n\n /* Actions Defaults */\n --action-plain-border-color: #ccc;\n --action-plain-background-hover: #ddd;\n\n --action-accent-background: #007bff;\n --action-accent-background-hover: #0056b3;\n --action-accent-text-color: #fff;\n\n --action-red-background: #dc3545;\n --action-red-background-hover: #bd2130;\n --action-red-text-color: #fff;\n\n /* Callout Defaults */\n --callout-accent-background: #007bff33;\n --callout-accent-border: #007bff99;\n --callout-accent-text: #007bff;\n\n --callout-red-background: #dc354533;\n --callout-red-border: #dc354599;\n --callout-red-text: #dc3545;\n\n --callout-orange-background: #fd7e1433;\n --callout-orange-border: #fd7e1499;\n --callout-orange-text: #fd7e14;\n\n /* Visualiser Defaults */\n --visualiser-background: #e0e0e0;\n --visualiser-level-color: #28a745;\n }\n\n @media (prefers-color-scheme: dark) {\n :host {\n /* Component Dark */\n --component-text-color: #eee;\n\n /* Card Dark */\n --card-background: #333;\n --card-border-color: #555;\n\n /* Actions Dark */\n --action-plain-border-color: #555;\n --action-plain-background: #333;\n --action-plain-background-hover: #444;\n\n --action-accent-background: #0056b3;\n --action-accent-background-hover: #003d80;\n\n /* Visualiser Dark */\n --visualiser-background: #fff;\n }\n }\n :host {\n box-sizing: border-box;\n font-family: var(--component-font-family);\n color: var(--component-text-color);\n }\n`;\n\nexport default ThemeStyles;\n"]}
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/styles/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,WAAW,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiDtB,CAAC;AAEF,eAAe,WAAW,CAAC","sourcesContent":["import { css } from 'lit';\n\nconst ThemeStyles = css`\n :host {\n color-scheme: light dark;\n /* Component Defaults */\n --component-font-family: 'Segoe UI', Roboto, sans-serif;\n --component-text-color: light-dark(#333, #eee);\n\n /* Card Defaults */\n --card-background: light-dark(#fff, #333);\n --card-border-color: light-dark(#ddd, #555);\n --card-padding: 4px;\n --card-border-radius: 8px;\n --card-inner-border-radius: 6px;\n --card-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);\n\n /* Actions Defaults */\n --action-plain-border-color: light-dark(#ccc, #555);\n --action-plain-background-hover: light-dark(#ddd, #444);\n\n --action-accent-background: light-dark(#007bff, #0056b3);\n --action-accent-background-hover: light-dark(#0056b3, #003d80);\n --action-accent-text-color: #fff;\n\n --action-red-background: light-dark(#dc3545, #bd2130);\n --action-red-background-hover: light-dark(#bd2130, #a71c24);\n --action-red-text-color: #fff;\n\n /* Callout Defaults */\n --callout-info-background: light-dark(#007bff33, #0056b333);\n --callout-info-border: light-dark(#007bff99, #0056b399);\n --callout-info-text: light-dark(#007bff, #0056b3);\n\n --callout-error-background: light-dark(#dc354533, #bd213033);\n --callout-error-border: light-dark(#dc354599, #bd213099);\n --callout-error-text: light-dark(#dc3545, #bd2130);\n\n --callout-warn-background: light-dark(#fd7e1433, #e06c1233);\n --callout-warn-border: light-dark(#fd7e1499, #e06c1299);\n --callout-warn-text: light-dark(#fd7e14, #e06c12);\n\n /* Visualiser Defaults */\n --visualiser-background: light-dark(#e0e0e0, #fff);\n }\n\n :host {\n box-sizing: border-box;\n font-family: var(--component-font-family);\n color: var(--component-text-color);\n }\n`;\n\nexport default ThemeStyles;\n"]}
package/dist/types.d.ts CHANGED
@@ -19,8 +19,8 @@ export interface DictationConfig {
19
19
  }
20
20
  export type PartialDictationConfig = Partial<DictationConfig>;
21
21
  export interface ServerConfig {
22
- environment?: string;
23
- tenant?: string;
24
- token?: string;
22
+ environment: string;
23
+ tenant: string;
24
+ accessToken: string;
25
25
  }
26
26
  export {};
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type RecordingState =\n | 'initializing'\n | 'recording'\n | 'stopping'\n | 'stopped';\n\ninterface CommandVariable {\n key: string;\n type: 'enum' | 'string';\n enum?: string[];\n}\n\nexport interface Command {\n id: string;\n phrases: string[];\n variables?: CommandVariable[];\n}\n\nexport interface DictationConfig {\n primaryLanguage: string;\n interimResults: boolean;\n spokenPunctuation: boolean;\n automaticPunctuation: boolean;\n model: string;\n commands?: Command[];\n}\n\nexport type PartialDictationConfig = Partial<DictationConfig>;\n\nexport interface ServerConfig {\n environment?: string;\n tenant?: string;\n token?: string;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type RecordingState =\n | 'initializing'\n | 'recording'\n | 'stopping'\n | 'stopped';\n\ninterface CommandVariable {\n key: string;\n type: 'enum' | 'string';\n enum?: string[];\n}\n\nexport interface Command {\n id: string;\n phrases: string[];\n variables?: CommandVariable[];\n}\n\nexport interface DictationConfig {\n primaryLanguage: string;\n interimResults: boolean;\n spokenPunctuation: boolean;\n automaticPunctuation: boolean;\n model: string;\n commands?: Command[];\n}\n\nexport type PartialDictationConfig = Partial<DictationConfig>;\n\nexport interface ServerConfig {\n environment: string;\n tenant: string;\n accessToken: string;\n}\n"]}
package/dist/utils.d.ts CHANGED
@@ -41,7 +41,7 @@ export declare function getAudioDevices(): Promise<{
41
41
  * @returns An object containing:
42
42
  * - `environment`: The extracted environment from the issuer URL.
43
43
  * - `tenant`: The extracted tenant from the issuer URL.
44
- * - `token`: The original token string.
44
+ * - `accessToken`: The original token string.
45
45
  * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
46
46
  *
47
47
  * @throws Will throw an error if:
@@ -53,6 +53,6 @@ export declare function getAudioDevices(): Promise<{
53
53
  export declare function decodeToken(token: string): {
54
54
  environment: string;
55
55
  tenant: string;
56
- token: string;
56
+ accessToken: string;
57
57
  } | undefined;
58
58
  export declare function getMediaStream(deviceId?: string): Promise<MediaStream>;
package/dist/utils.js CHANGED
@@ -86,7 +86,7 @@ export async function getAudioDevices() {
86
86
  * @returns An object containing:
87
87
  * - `environment`: The extracted environment from the issuer URL.
88
88
  * - `tenant`: The extracted tenant from the issuer URL.
89
- * - `token`: The original token string.
89
+ * - `accessToken`: The original token string.
90
90
  * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
91
91
  *
92
92
  * @throws Will throw an error if:
@@ -139,7 +139,7 @@ export function decodeToken(token) {
139
139
  return {
140
140
  environment: match[2],
141
141
  tenant: match[3],
142
- token,
142
+ accessToken: token,
143
143
  };
144
144
  }
145
145
  }
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-console */\r\n/**\r\n * Returns the localized name of a language given its BCP-47 code.\r\n *\r\n * @param languageCode - The BCP-47 language code (e.g. \"en\")\r\n * @returns The localized language name (e.g. \"English\") or the original code if unavailable.\r\n */\r\nexport function getLanguageName(languageCode: string): string {\r\n const userLocale = navigator.language || 'en';\r\n const displayNames = new Intl.DisplayNames([userLocale], {\r\n type: 'language',\r\n });\r\n const languageName = displayNames.of(languageCode);\r\n return languageName || languageCode;\r\n}\r\n\r\n/**\r\n * Requests access to the microphone.\r\n *\r\n * This function checks if the microphone permission is in \"prompt\" state, then requests\r\n * access and stops any active tracks immediately. It also logs if permission is already granted.\r\n *\r\n * @returns A promise that resolves when the permission request is complete.\r\n */\r\nexport async function requestMicAccess(): Promise<void> {\r\n try {\r\n // Fallback if Permissions API is not available\r\n if (!navigator.permissions) {\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\r\n stream.getTracks().forEach(track => track.stop());\r\n return;\r\n }\r\n\r\n const permissionStatus = await navigator.permissions.query({\r\n // eslint-disable-next-line no-undef\r\n name: 'microphone' as PermissionName,\r\n });\r\n\r\n if (permissionStatus.state === 'prompt') {\r\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\r\n stream.getTracks().forEach(track => track.stop());\r\n } else if (permissionStatus.state === 'denied') {\r\n console.warn('Microphone permission is denied.');\r\n }\r\n } catch (error) {\r\n console.error('Error checking/requesting microphone permission:', error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves available audio input devices.\r\n *\r\n * This function uses the mediaDevices API to enumerate devices and filters out those\r\n * which are audio inputs. In some browsers, you may need to request user media before\r\n * device labels are populated.\r\n *\r\n * @returns A promise that resolves with an object containing:\r\n * - `devices`: an array of MediaDeviceInfo objects for audio inputs.\r\n * - `defaultDeviceId`: the deviceId of the first audio input, if available.\r\n */\r\nexport async function getAudioDevices(): Promise<{\r\n devices: MediaDeviceInfo[];\r\n defaultDevice?: MediaDeviceInfo;\r\n}> {\r\n if (!navigator.mediaDevices?.enumerateDevices) {\r\n console.error('Media devices API not supported.');\r\n return { devices: [] };\r\n }\r\n\r\n await requestMicAccess();\r\n\r\n try {\r\n // Optionally: await navigator.mediaDevices.getUserMedia({ audio: true });\r\n const devices = await navigator.mediaDevices.enumerateDevices();\r\n const audioDevices = devices.filter(device => device.kind === 'audioinput');\r\n const defaultDevice = audioDevices.length > 0 ? audioDevices[0] : undefined;\r\n return { devices: audioDevices, defaultDevice };\r\n } catch (error) {\r\n console.error('Error enumerating devices:', error);\r\n return { devices: [] };\r\n }\r\n}\r\n\r\n/**\r\n * Decodes a JWT token and extracts environment and tenant details from its issuer URL.\r\n *\r\n * This function assumes the JWT token follows the standard header.payload.signature format.\r\n * It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex\r\n * to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:\r\n * https://keycloak.{environment}.corti.app/realms/{tenant}.\r\n *\r\n * @param token - A JSON Web Token (JWT) string.\r\n * @returns An object containing:\r\n * - `environment`: The extracted environment from the issuer URL.\r\n * - `tenant`: The extracted tenant from the issuer URL.\r\n * - `token`: The original token string.\r\n * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.\r\n *\r\n * @throws Will throw an error if:\r\n * - The token format is invalid.\r\n * - The base64 decoding or URI decoding fails.\r\n * - The JSON payload is invalid.\r\n * - The token payload does not contain an issuer (iss) field.\r\n */\r\nexport function decodeToken(token: string) {\r\n // Validate the token structure (should contain at least header and payload parts)\r\n const parts = token.split('.');\r\n if (parts.length < 2) {\r\n throw new Error('Invalid token format');\r\n }\r\n\r\n // Retrieve the payload (second part) of the JWT token\r\n const base64Url = parts[1];\r\n\r\n // Replace URL-safe characters to match standard base64 encoding\r\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\r\n\r\n // Decode the base64 string into a JSON string\r\n let jsonPayload: string;\r\n try {\r\n jsonPayload = decodeURIComponent(\r\n atob(base64)\r\n .split('')\r\n .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\r\n .join(''),\r\n );\r\n } catch (error) {\r\n throw new Error('Failed to decode token payload');\r\n }\r\n\r\n // Parse the JSON string to obtain token details\r\n let tokenDetails: { iss: string; [key: string]: unknown };\r\n try {\r\n tokenDetails = JSON.parse(jsonPayload);\r\n } catch (error) {\r\n throw new Error('Invalid JSON payload in token');\r\n }\r\n\r\n // Extract the issuer URL from the token details\r\n const issuerUrl: string = tokenDetails.iss;\r\n if (!issuerUrl) {\r\n throw new Error('Token payload does not contain an issuer (iss) field');\r\n }\r\n\r\n // Regex to extract environment and tenant from issuer URL:\r\n // Expected format: https://keycloak.{environment}.corti.app/realms/{tenant}\r\n // Note: Unnecessary escapes in character classes have been removed.\r\n const regex =\r\n /^https:\\/\\/(keycloak|auth)\\.([^.]+)\\.corti\\.app\\/realms\\/([^/]+)/;\r\n const match = issuerUrl.match(regex);\r\n\r\n // If the issuer URL matches the expected pattern, return the extracted values along with the token\r\n if (match) {\r\n return {\r\n environment: match[2],\r\n tenant: match[3],\r\n token,\r\n };\r\n }\r\n}\r\n\r\nexport async function getMediaStream(deviceId?: string): Promise<MediaStream> {\r\n if (!deviceId) {\r\n throw new Error('No device ID provided');\r\n }\r\n\r\n if (deviceId === 'display_audio') {\r\n const stream = await navigator.mediaDevices.getDisplayMedia({\r\n audio: true,\r\n video: true,\r\n });\r\n stream.getTracks().forEach(track => {\r\n if (track.kind === 'video') {\r\n stream.removeTrack(track);\r\n }\r\n });\r\n return stream;\r\n }\r\n\r\n // Get media stream and initialize audio service.\r\n const constraints: MediaStreamConstraints =\r\n deviceId !== 'default'\r\n ? { audio: { deviceId: { exact: deviceId } } }\r\n : { audio: true };\r\n\r\n return await navigator.mediaDevices.getUserMedia(constraints);\r\n}\r\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable no-console */\n/**\n * Returns the localized name of a language given its BCP-47 code.\n *\n * @param languageCode - The BCP-47 language code (e.g. \"en\")\n * @returns The localized language name (e.g. \"English\") or the original code if unavailable.\n */\nexport function getLanguageName(languageCode: string): string {\n const userLocale = navigator.language || 'en';\n const displayNames = new Intl.DisplayNames([userLocale], {\n type: 'language',\n });\n const languageName = displayNames.of(languageCode);\n return languageName || languageCode;\n}\n\n/**\n * Requests access to the microphone.\n *\n * This function checks if the microphone permission is in \"prompt\" state, then requests\n * access and stops any active tracks immediately. It also logs if permission is already granted.\n *\n * @returns A promise that resolves when the permission request is complete.\n */\nexport async function requestMicAccess(): Promise<void> {\n try {\n // Fallback if Permissions API is not available\n if (!navigator.permissions) {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n return;\n }\n\n const permissionStatus = await navigator.permissions.query({\n // eslint-disable-next-line no-undef\n name: 'microphone' as PermissionName,\n });\n\n if (permissionStatus.state === 'prompt') {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach(track => track.stop());\n } else if (permissionStatus.state === 'denied') {\n console.warn('Microphone permission is denied.');\n }\n } catch (error) {\n console.error('Error checking/requesting microphone permission:', error);\n }\n}\n\n/**\n * Retrieves available audio input devices.\n *\n * This function uses the mediaDevices API to enumerate devices and filters out those\n * which are audio inputs. In some browsers, you may need to request user media before\n * device labels are populated.\n *\n * @returns A promise that resolves with an object containing:\n * - `devices`: an array of MediaDeviceInfo objects for audio inputs.\n * - `defaultDeviceId`: the deviceId of the first audio input, if available.\n */\nexport async function getAudioDevices(): Promise<{\n devices: MediaDeviceInfo[];\n defaultDevice?: MediaDeviceInfo;\n}> {\n if (!navigator.mediaDevices?.enumerateDevices) {\n console.error('Media devices API not supported.');\n return { devices: [] };\n }\n\n await requestMicAccess();\n\n try {\n // Optionally: await navigator.mediaDevices.getUserMedia({ audio: true });\n const devices = await navigator.mediaDevices.enumerateDevices();\n const audioDevices = devices.filter(device => device.kind === 'audioinput');\n const defaultDevice = audioDevices.length > 0 ? audioDevices[0] : undefined;\n return { devices: audioDevices, defaultDevice };\n } catch (error) {\n console.error('Error enumerating devices:', error);\n return { devices: [] };\n }\n}\n\n/**\n * Decodes a JWT token and extracts environment and tenant details from its issuer URL.\n *\n * This function assumes the JWT token follows the standard header.payload.signature format.\n * It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex\n * to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:\n * https://keycloak.{environment}.corti.app/realms/{tenant}.\n *\n * @param token - A JSON Web Token (JWT) string.\n * @returns An object containing:\n * - `environment`: The extracted environment from the issuer URL.\n * - `tenant`: The extracted tenant from the issuer URL.\n * - `accessToken`: The original token string.\n * If the issuer URL doesn't match the expected format, the function returns the full decoded token details.\n *\n * @throws Will throw an error if:\n * - The token format is invalid.\n * - The base64 decoding or URI decoding fails.\n * - The JSON payload is invalid.\n * - The token payload does not contain an issuer (iss) field.\n */\nexport function decodeToken(token: string) {\n // Validate the token structure (should contain at least header and payload parts)\n const parts = token.split('.');\n if (parts.length < 2) {\n throw new Error('Invalid token format');\n }\n\n // Retrieve the payload (second part) of the JWT token\n const base64Url = parts[1];\n\n // Replace URL-safe characters to match standard base64 encoding\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n\n // Decode the base64 string into a JSON string\n let jsonPayload: string;\n try {\n jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n } catch (error) {\n throw new Error('Failed to decode token payload');\n }\n\n // Parse the JSON string to obtain token details\n let tokenDetails: { iss: string; [key: string]: unknown };\n try {\n tokenDetails = JSON.parse(jsonPayload);\n } catch (error) {\n throw new Error('Invalid JSON payload in token');\n }\n\n // Extract the issuer URL from the token details\n const issuerUrl: string = tokenDetails.iss;\n if (!issuerUrl) {\n throw new Error('Token payload does not contain an issuer (iss) field');\n }\n\n // Regex to extract environment and tenant from issuer URL:\n // Expected format: https://keycloak.{environment}.corti.app/realms/{tenant}\n // Note: Unnecessary escapes in character classes have been removed.\n const regex =\n /^https:\\/\\/(keycloak|auth)\\.([^.]+)\\.corti\\.app\\/realms\\/([^/]+)/;\n const match = issuerUrl.match(regex);\n\n // If the issuer URL matches the expected pattern, return the extracted values along with the token\n if (match) {\n return {\n environment: match[2],\n tenant: match[3],\n accessToken: token,\n };\n }\n}\n\nexport async function getMediaStream(deviceId?: string): Promise<MediaStream> {\n if (!deviceId) {\n throw new Error('No device ID provided');\n }\n\n if (deviceId === 'display_audio') {\n const stream = await navigator.mediaDevices.getDisplayMedia({\n audio: true,\n video: true,\n });\n stream.getTracks().forEach(track => {\n if (track.kind === 'video') {\n stream.removeTrack(track);\n }\n });\n return stream;\n }\n\n // Get media stream and initialize audio service.\n const constraints: MediaStreamConstraints =\n deviceId !== 'default'\n ? { audio: { deviceId: { exact: deviceId } } }\n : { audio: true };\n\n return await navigator.mediaDevices.getUserMedia(constraints);\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@corti/dictation-web",
3
3
  "description": "Web component for Corti Dictation",
4
4
  "author": "Corti ApS",
5
- "version": "0.1.7",
5
+ "version": "0.1.8",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",