@corti/dictation-web 0.1.3 → 0.1.5

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
@@ -1,11 +1,11 @@
1
1
  # Corti Dictation SDK
2
2
 
3
3
  ## Overview
4
+
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.
5
6
 
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.
7
8
 
8
-
9
9
  ## Installation
10
10
 
11
11
  Include the SDK in your project by importing the JavaScript module:
@@ -14,6 +14,13 @@ Include the SDK in your project by importing the JavaScript module:
14
14
  npm i @corti/dictation-web
15
15
  ```
16
16
 
17
+ ```html
18
+ <script
19
+ src="https://cdn.jsdelivr.net/npm/@corti/dictation-web/dist/bundle.min.js"
20
+ preload
21
+ type="module"
22
+ ></script>
23
+ ```
17
24
 
18
25
  ## Usage
19
26
 
@@ -21,74 +28,74 @@ npm i @corti/dictation-web
21
28
 
22
29
  🚀 [Hosted Demo](https://codepen.io/hccullen/pen/OPJmxQR)
23
30
 
24
-
25
31
  ### Basic Example
26
32
 
27
33
  ```html
28
34
  <!DOCTYPE html>
29
35
  <html lang="en">
30
- <head>
31
- <meta charset="UTF-8">
32
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
- </head>
34
- <body>
35
- <script src="https://cdn.jsdelivr.net/npm/@corti/dictation-web/dist/index.min.js"></script>
36
- <corti-dictation authToken="xyz"></corti-dictation>
37
- <textarea id="transcript" placeholder="Transcript will appear here..."></textarea>
38
-
39
- <script>
40
- const dictationEl = document.getElementById('transcript')
41
- // Listen for events
42
- dictationEl.addEventListener('transcript', (e) => {
43
- document.getElementById('transcript').value += e.detail.data.text + ' ';
44
- });
45
- </script>
46
- </body>
36
+ <body>
37
+ <script
38
+ src="https://cdn.jsdelivr.net/npm/@corti/dictation-web/dist/bundle.min.js"
39
+ preload
40
+ type="module"
41
+ ></script>
42
+ <corti-dictation authToken="xyz"></corti-dictation>
43
+ <textarea
44
+ id="transcript"
45
+ placeholder="Transcript will appear here..."
46
+ ></textarea>
47
+
48
+ <script>
49
+ const dictationEl = document.getElementById('transcript');
50
+ // Listen for events
51
+ dictationEl.addEventListener('transcript', e => {
52
+ document.getElementById('transcript').value += e.detail.data.text + ' ';
53
+ });
54
+ </script>
55
+ </body>
47
56
  </html>
48
57
  ```
49
58
 
50
-
51
59
  ## API Reference
52
60
 
53
61
  ### Properties
54
62
 
55
- | Property | Type | Description |
56
- |-----------------|---------|-------------|
57
- | `devices` | Array | List of available recording devices. |
58
- | `recordingState` | String | Current state of recording (`stopped`, `recording`). |
59
- | `dictationConfig` | Object | Configuration settings for dictation. |
60
- | `authToken` | String | Authentication token from OAuth server |
63
+ | Property | Type | Description |
64
+ | ----------------- | ------ | ---------------------------------------------------- |
65
+ | `devices` | Array | List of available recording devices. |
66
+ | `recordingState` | String | Current state of recording (`stopped`, `recording`). |
67
+ | `dictationConfig` | Object | Configuration settings for dictation. |
68
+ | `authToken` | String | Authentication token from OAuth server |
61
69
 
62
70
  ### Methods
63
71
 
64
- | Method | Description |
65
- |---------------------|-------------|
72
+ | Method | Description |
73
+ | ------------------- | -------------------------- |
66
74
  | `toggleRecording()` | Starts or stops recording. |
67
75
 
68
76
  ### Events
69
77
 
70
- | Event | Description |
71
- |----------------------------|-------------|
72
- | `recording-state-changed` | Fired when the recording state changes. `detail.state` contains the new state. |
78
+ | Event | Description |
79
+ | -------------------------- | --------------------------------------------------------------------------------------------- |
80
+ | `recording-state-changed` | Fired when the recording state changes. `detail.state` contains the new state. |
73
81
  | `recording-device-changed` | Fired when the user switches recording devices. `detail.deviceId` contains the new device ID. |
74
- | `transcript` | Fired when a new transcript is received. `detail.data.text` contains the transcribed text. |
75
- | `audio-level-changed` | Fired when the input audio level changes. `detail.audioLevel` contains the new level. |
76
-
82
+ | `transcript` | Fired when a new transcript is received. `detail.data.text` contains the transcribed text. |
83
+ | `audio-level-changed` | Fired when the input audio level changes. `detail.audioLevel` contains the new level. |
84
+ | `error` | Fired on error. `detail` contains the full error. |
77
85
 
78
86
  ## Authentication
79
87
 
80
88
  This SDK does not handle OAuth 2.0 authentication. The client must provide an API key or access token as a string in `authToken`.
81
89
 
82
-
83
90
  ## Notes
91
+
84
92
  - Works in modern browsers that support Web Components and MediaRecorder API.
85
93
  - Supports dark and light mode based on browser preference.
86
94
 
87
-
88
95
  ## License
89
- This SDK is not licensed.
90
96
 
97
+ This SDK is not licensed.
91
98
 
92
99
  ## Support
93
- For issues or questions, contact **Corti Support** at [support@corti.ai](mailto:support@corti.ai).
94
100
 
101
+ For issues or questions, contact **Corti Support** at [support@corti.ai](mailto:support@corti.ai).
@@ -44,7 +44,7 @@ export class RecorderManager extends EventTarget {
44
44
  }
45
45
  catch (error) {
46
46
  this.dispatchCustomEvent('error', error);
47
- this._updateRecordingState('stopped');
47
+ this.stopRecording();
48
48
  return;
49
49
  }
50
50
  // Forward dictation service events.
@@ -1 +1 @@
1
- {"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAudioDevices } from './utils.js';\nimport { AudioService } from './audioService.js';\nimport { DictationService } from './DictationService.js';\nimport type { DictationConfig, RecordingState } from './types.js';\n\nexport class RecorderManager extends EventTarget {\n public devices: MediaDeviceInfo[] = [];\n\n public selectedDevice: string = '';\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 async initialize() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n this.selectedDevice = deviceResponse.defaultDeviceId || '';\n return deviceResponse;\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 async startRecording(params: {\n dictationConfig: DictationConfig;\n authToken: string;\n }): Promise<void> {\n this._updateRecordingState('initializing');\n\n // Get media stream and initialize audio service.\n try {\n this._mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { deviceId: this.selectedDevice },\n });\n this._audioService = new AudioService(this._mediaStream);\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this._updateRecordingState('stopped');\n return;\n }\n\n // Initialize dictation service.\n try {\n this._dictationService = new DictationService(this._mediaStream, params);\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this._updateRecordingState('stopped');\n return;\n }\n\n // Forward dictation service events.\n this._dictationService.addEventListener('error', e =>\n this.dispatchCustomEvent('error', (e as CustomEvent).detail),\n );\n this._dictationService.addEventListener('stream-closed', () =>\n this.stopRecording(),\n );\n this._dictationService.addEventListener('transcript', e =>\n this.dispatchCustomEvent('transcript', (e as CustomEvent).detail),\n );\n\n this._dictationService.startRecording();\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 this._mediaStream = null;\n }\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"]}
1
+ {"version":3,"file":"RecorderManager.js","sourceRoot":"","sources":["../src/RecorderManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAudioDevices } from './utils.js';\nimport { AudioService } from './audioService.js';\nimport { DictationService } from './DictationService.js';\nimport type { DictationConfig, RecordingState } from './types.js';\n\nexport class RecorderManager extends EventTarget {\n public devices: MediaDeviceInfo[] = [];\n\n public selectedDevice: string = '';\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 async initialize() {\n const deviceResponse = await getAudioDevices();\n this.devices = deviceResponse.devices;\n this.selectedDevice = deviceResponse.defaultDeviceId || '';\n return deviceResponse;\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 async startRecording(params: {\n dictationConfig: DictationConfig;\n authToken: string;\n }): Promise<void> {\n this._updateRecordingState('initializing');\n\n // Get media stream and initialize audio service.\n try {\n this._mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: { deviceId: this.selectedDevice },\n });\n this._audioService = new AudioService(this._mediaStream);\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this._updateRecordingState('stopped');\n return;\n }\n\n // Initialize dictation service.\n try {\n this._dictationService = new DictationService(this._mediaStream, params);\n } catch (error) {\n this.dispatchCustomEvent('error', error);\n this.stopRecording();\n return;\n }\n\n // Forward dictation service events.\n this._dictationService.addEventListener('error', e =>\n this.dispatchCustomEvent('error', (e as CustomEvent).detail),\n );\n this._dictationService.addEventListener('stream-closed', () =>\n this.stopRecording(),\n );\n this._dictationService.addEventListener('transcript', e =>\n this.dispatchCustomEvent('transcript', (e as CustomEvent).detail),\n );\n\n this._dictationService.startRecording();\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 this._mediaStream = null;\n }\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
@@ -1,11 +1,3 @@
1
- // node_modules/tslib/tslib.es6.mjs
2
- function __decorate(decorators, target, key, desc) {
3
- var c4 = arguments.length, r7 = c4 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d3;
4
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r7 = Reflect.decorate(decorators, target, key, desc);
5
- else for (var i5 = decorators.length - 1; i5 >= 0; i5--) if (d3 = decorators[i5]) r7 = (c4 < 3 ? d3(r7) : c4 > 3 ? d3(target, key, r7) : d3(target, key)) || r7;
6
- return c4 > 3 && r7 && Object.defineProperty(target, key, r7), r7;
7
- }
8
-
9
1
  // node_modules/@lit/reactive-element/css-tag.js
10
2
  var t = globalThis;
11
3
  var e = t.ShadowRoot && (void 0 === t.ShadyCSS || t.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype;
@@ -645,14 +637,12 @@ function decodeToken(token) {
645
637
  }
646
638
  const base64Url = parts[1];
647
639
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
648
- console.log("Decoded base64:", base64);
649
640
  let jsonPayload;
650
641
  try {
651
642
  jsonPayload = decodeURIComponent(atob(base64).split("").map((c4) => "%" + ("00" + c4.charCodeAt(0).toString(16)).slice(-2)).join(""));
652
643
  } catch (error) {
653
644
  throw new Error("Failed to decode token payload");
654
645
  }
655
- console.log("Decoded payload:", jsonPayload);
656
646
  let tokenDetails;
657
647
  try {
658
648
  tokenDetails = JSON.parse(jsonPayload);
@@ -665,9 +655,7 @@ function decodeToken(token) {
665
655
  }
666
656
  const regex = /^https:\/\/(keycloak|auth)\.([^.]+)\.corti\.app\/realms\/([^/]+)/;
667
657
  const match = issuerUrl.match(regex);
668
- console.log("Matched URL pattern:", match);
669
658
  if (match) {
670
- console.log("Matched URL pattern:", match);
671
659
  return {
672
660
  environment: match[2],
673
661
  tenant: match[3],
@@ -705,24 +693,26 @@ var DictationService = class extends EventTarget {
705
693
  this.mediaRecorder = new MediaRecorder(mediaStream);
706
694
  this.authToken = authToken;
707
695
  this.dictationConfig = dictationConfig;
696
+ const config = decodeToken(this.authToken);
697
+ if (!config) {
698
+ throw new Error("Invalid token");
699
+ }
700
+ this.serverConfig = config;
708
701
  this.mediaRecorder.ondataavailable = (event) => {
709
702
  if (this.webSocket?.readyState === WebSocket.OPEN) {
710
703
  this.webSocket.send(event.data);
711
704
  }
712
705
  };
713
706
  }
707
+ dispatchCustomEvent(eventName, detail) {
708
+ this.dispatchEvent(new CustomEvent(eventName, {
709
+ detail,
710
+ bubbles: true,
711
+ composed: true
712
+ }));
713
+ }
714
714
  startRecording() {
715
- const serverConfig = decodeToken(this.authToken);
716
- if (!serverConfig) {
717
- this.dispatchEvent(new CustomEvent("error", {
718
- detail: "Invalid token",
719
- bubbles: true,
720
- composed: true
721
- }));
722
- return;
723
- }
724
- console.log("serverConfig:", serverConfig);
725
- const url = `wss://api.${serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${serverConfig.tenant}&token=Bearer%20${this.authToken}`;
715
+ const url = `wss://api.${this.serverConfig.environment}.corti.app/audio-bridge/v2/transcribe?tenant-name=${this.serverConfig.tenant}&token=Bearer%20${this.authToken}`;
726
716
  this.webSocket = new WebSocket(url);
727
717
  this.webSocket.onopen = () => {
728
718
  this.webSocket.send(JSON.stringify({
@@ -735,43 +725,29 @@ var DictationService = class extends EventTarget {
735
725
  if (message.type === "config") {
736
726
  this.mediaRecorder.start(250);
737
727
  } else if (message.type === "transcript") {
738
- this.dispatchEvent(new CustomEvent("transcript", {
739
- detail: message,
740
- bubbles: true,
741
- composed: true
742
- }));
728
+ this.dispatchCustomEvent("transcript", message);
743
729
  }
744
730
  };
745
731
  this.webSocket.onerror = (event) => {
746
- this.dispatchEvent(new CustomEvent("error", {
747
- detail: event,
748
- bubbles: true,
749
- composed: true
750
- }));
732
+ this.dispatchCustomEvent("error", event);
751
733
  };
752
734
  this.webSocket.onclose = (event) => {
753
- this.dispatchEvent(new CustomEvent("stream-closed", {
754
- detail: event,
755
- bubbles: true,
756
- composed: true
757
- }));
735
+ this.dispatchCustomEvent("stream-closed", event);
758
736
  };
759
737
  }
760
738
  async stopRecording() {
761
739
  this.mediaRecorder.stop();
762
740
  if (this.webSocket?.readyState === WebSocket.OPEN) {
763
- this.webSocket.send(JSON.stringify({
764
- type: "end"
765
- }));
741
+ this.webSocket.send(JSON.stringify({ type: "end" }));
766
742
  }
767
- const timeOut = setTimeout(() => {
743
+ const timeout = setTimeout(() => {
768
744
  if (this.webSocket?.readyState === WebSocket.OPEN) {
769
745
  this.webSocket.close();
770
746
  }
771
747
  }, 1e4);
772
748
  this.webSocket.onclose = () => {
773
749
  this.webSocket?.close();
774
- clearTimeout(timeOut);
750
+ clearTimeout(timeout);
775
751
  };
776
752
  }
777
753
  };
@@ -793,6 +769,13 @@ var RecorderManager = class extends EventTarget {
793
769
  this.selectedDevice = deviceResponse.defaultDeviceId || "";
794
770
  return deviceResponse;
795
771
  }
772
+ dispatchCustomEvent(eventName, detail) {
773
+ this.dispatchEvent(new CustomEvent(eventName, {
774
+ detail,
775
+ bubbles: true,
776
+ composed: true
777
+ }));
778
+ }
796
779
  async startRecording(params) {
797
780
  this._updateRecordingState("initializing");
798
781
  try {
@@ -800,36 +783,26 @@ var RecorderManager = class extends EventTarget {
800
783
  audio: { deviceId: this.selectedDevice }
801
784
  });
802
785
  this._audioService = new AudioService(this._mediaStream);
803
- this._dictationService = new DictationService(this._mediaStream, params);
804
- this._dictationService.addEventListener("error", (e5) => this.dispatchEvent(new CustomEvent("error", {
805
- detail: e5.detail,
806
- bubbles: true,
807
- composed: true
808
- })));
809
- this._dictationService.addEventListener("stream-closed", () => this.stopRecording());
810
- this._dictationService.addEventListener("transcript", (e5) => this.dispatchEvent(new CustomEvent("transcript", {
811
- detail: e5.detail,
812
- bubbles: true,
813
- composed: true
814
- })));
815
786
  } catch (error) {
816
- this.dispatchEvent(new CustomEvent("error", {
817
- detail: error,
818
- bubbles: true,
819
- composed: true
820
- }));
787
+ this.dispatchCustomEvent("error", error);
821
788
  this._updateRecordingState("stopped");
822
789
  return;
823
790
  }
824
- this._dictationService?.startRecording();
791
+ try {
792
+ this._dictationService = new DictationService(this._mediaStream, params);
793
+ } catch (error) {
794
+ this.dispatchCustomEvent("error", error);
795
+ this.stopRecording();
796
+ return;
797
+ }
798
+ this._dictationService.addEventListener("error", (e5) => this.dispatchCustomEvent("error", e5.detail));
799
+ this._dictationService.addEventListener("stream-closed", () => this.stopRecording());
800
+ this._dictationService.addEventListener("transcript", (e5) => this.dispatchCustomEvent("transcript", e5.detail));
801
+ this._dictationService.startRecording();
825
802
  this._updateRecordingState("recording");
826
803
  this._visualiserInterval = window.setInterval(() => {
827
804
  const level = this._audioService ? this._audioService.getAudioLevel() * 3 : 0;
828
- this.dispatchEvent(new CustomEvent("audio-level-changed", {
829
- detail: { audioLevel: level },
830
- bubbles: true,
831
- composed: true
832
- }));
805
+ this.dispatchCustomEvent("audio-level-changed", { audioLevel: level });
833
806
  }, 150);
834
807
  }
835
808
  async stopRecording() {
@@ -986,6 +959,12 @@ var CalloutStyles = i`
986
959
  var callout_default = CalloutStyles;
987
960
 
988
961
  // dist/components/settings-menu.js
962
+ var __decorate = function(decorators, target, key, desc) {
963
+ var c4 = arguments.length, r7 = c4 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d3;
964
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r7 = Reflect.decorate(decorators, target, key, desc);
965
+ else for (var i5 = decorators.length - 1; i5 >= 0; i5--) if (d3 = decorators[i5]) r7 = (c4 < 3 ? d3(r7) : c4 > 3 ? d3(target, key, r7) : d3(target, key)) || r7;
966
+ return c4 > 3 && r7 && Object.defineProperty(target, key, r7), r7;
967
+ };
989
968
  var SettingsMenu = class SettingsMenu2 extends r4 {
990
969
  constructor() {
991
970
  super(...arguments);
@@ -1122,6 +1101,12 @@ SettingsMenu = __decorate([
1122
1101
  ], SettingsMenu);
1123
1102
 
1124
1103
  // dist/components/audio-visualiser.js
1104
+ var __decorate2 = function(decorators, target, key, desc) {
1105
+ var c4 = arguments.length, r7 = c4 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d3;
1106
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r7 = Reflect.decorate(decorators, target, key, desc);
1107
+ else for (var i5 = decorators.length - 1; i5 >= 0; i5--) if (d3 = decorators[i5]) r7 = (c4 < 3 ? d3(r7) : c4 > 3 ? d3(target, key, r7) : d3(target, key)) || r7;
1108
+ return c4 > 3 && r7 && Object.defineProperty(target, key, r7), r7;
1109
+ };
1125
1110
  var AudioVisualiser = class AudioVisualiser2 extends r4 {
1126
1111
  constructor() {
1127
1112
  super(...arguments);
@@ -1167,17 +1152,23 @@ AudioVisualiser.styles = i`
1167
1152
  opacity: 1;
1168
1153
  }
1169
1154
  `;
1170
- __decorate([
1155
+ __decorate2([
1171
1156
  n4({ type: Number })
1172
1157
  ], AudioVisualiser.prototype, "level", void 0);
1173
- __decorate([
1158
+ __decorate2([
1174
1159
  n4({ type: Boolean })
1175
1160
  ], AudioVisualiser.prototype, "active", void 0);
1176
- AudioVisualiser = __decorate([
1161
+ AudioVisualiser = __decorate2([
1177
1162
  t3("audio-visualiser")
1178
1163
  ], AudioVisualiser);
1179
1164
 
1180
1165
  // dist/icons/icons.js
1166
+ var __decorate3 = function(decorators, target, key, desc) {
1167
+ var c4 = arguments.length, r7 = c4 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d3;
1168
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r7 = Reflect.decorate(decorators, target, key, desc);
1169
+ else for (var i5 = decorators.length - 1; i5 >= 0; i5--) if (d3 = decorators[i5]) r7 = (c4 < 3 ? d3(r7) : c4 > 3 ? d3(target, key, r7) : d3(target, key)) || r7;
1170
+ return c4 > 3 && r7 && Object.defineProperty(target, key, r7), r7;
1171
+ };
1181
1172
  var IconMicOn = class IconMicOn2 extends r4 {
1182
1173
  render() {
1183
1174
  return x`
@@ -1202,7 +1193,7 @@ var IconMicOn = class IconMicOn2 extends r4 {
1202
1193
  `;
1203
1194
  }
1204
1195
  };
1205
- IconMicOn = __decorate([
1196
+ IconMicOn = __decorate3([
1206
1197
  t3("icon-mic-on")
1207
1198
  ], IconMicOn);
1208
1199
  var IconMicOff = class IconMicOff2 extends r4 {
@@ -1230,7 +1221,7 @@ var IconMicOff = class IconMicOff2 extends r4 {
1230
1221
  </div>`;
1231
1222
  }
1232
1223
  };
1233
- IconMicOff = __decorate([
1224
+ IconMicOff = __decorate3([
1234
1225
  t3("icon-mic-off")
1235
1226
  ], IconMicOff);
1236
1227
  var IconRecording = class IconRecording2 extends r4 {
@@ -1256,7 +1247,7 @@ var IconRecording = class IconRecording2 extends r4 {
1256
1247
  `;
1257
1248
  }
1258
1249
  };
1259
- IconRecording = __decorate([
1250
+ IconRecording = __decorate3([
1260
1251
  t3("icon-recording")
1261
1252
  ], IconRecording);
1262
1253
  var IconSettings = class IconSettings2 extends r4 {
@@ -1282,7 +1273,7 @@ var IconSettings = class IconSettings2 extends r4 {
1282
1273
  </div>`;
1283
1274
  }
1284
1275
  };
1285
- IconSettings = __decorate([
1276
+ IconSettings = __decorate3([
1286
1277
  t3("icon-settings")
1287
1278
  ], IconSettings);
1288
1279
  var IconLoadingSpinner = class IconLoadingSpinner2 extends r4 {
@@ -1318,7 +1309,7 @@ IconLoadingSpinner.styles = i`
1318
1309
  animation: spin 1s linear infinite;
1319
1310
  }
1320
1311
  `;
1321
- IconLoadingSpinner = __decorate([
1312
+ IconLoadingSpinner = __decorate3([
1322
1313
  t3("icon-loading-spinner")
1323
1314
  ], IconLoadingSpinner);
1324
1315
 
@@ -1449,6 +1440,12 @@ var ComponentStyles = i`
1449
1440
  var ComponentStyles_default = ComponentStyles;
1450
1441
 
1451
1442
  // dist/CortiDictation.js
1443
+ var __decorate4 = function(decorators, target, key, desc) {
1444
+ var c4 = arguments.length, r7 = c4 < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d3;
1445
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r7 = Reflect.decorate(decorators, target, key, desc);
1446
+ else for (var i5 = decorators.length - 1; i5 >= 0; i5--) if (d3 = decorators[i5]) r7 = (c4 < 3 ? d3(r7) : c4 > 3 ? d3(target, key, r7) : d3(target, key)) || r7;
1447
+ return c4 > 3 && r7 && Object.defineProperty(target, key, r7), r7;
1448
+ };
1452
1449
  var CortiDictation = class extends r4 {
1453
1450
  constructor() {
1454
1451
  super(...arguments);
@@ -1557,19 +1554,19 @@ var CortiDictation = class extends r4 {
1557
1554
  }
1558
1555
  };
1559
1556
  CortiDictation.styles = [buttons_default, theme_default, ComponentStyles_default, callout_default];
1560
- __decorate([
1557
+ __decorate4([
1561
1558
  n4({ type: Array })
1562
1559
  ], CortiDictation.prototype, "devices", void 0);
1563
- __decorate([
1560
+ __decorate4([
1564
1561
  n4({ type: String, reflect: true })
1565
1562
  ], CortiDictation.prototype, "recordingState", void 0);
1566
- __decorate([
1563
+ __decorate4([
1567
1564
  n4({ type: Object })
1568
1565
  ], CortiDictation.prototype, "dictationConfig", void 0);
1569
- __decorate([
1566
+ __decorate4([
1570
1567
  n4({ type: String })
1571
1568
  ], CortiDictation.prototype, "authToken", void 0);
1572
- __decorate([
1569
+ __decorate4([
1573
1570
  r6()
1574
1571
  ], CortiDictation.prototype, "_audioLevel", void 0);
1575
1572
  var CortiDictation_default = CortiDictation;
package/package.json CHANGED
@@ -2,17 +2,21 @@
2
2
  "name": "@corti/dictation-web",
3
3
  "description": "Web component for Corti Dictation",
4
4
  "author": "Corti ApS",
5
- "version": "0.1.3",
5
+ "version": "0.1.5",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
9
9
  "exports": {
10
- ".": "./dist/index.js"
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/bundle.js"
11
12
  },
13
+ "unpkg": "dist/bundle.js",
12
14
  "files": ["dist"],
13
15
  "scripts": {
14
16
  "analyze": "cem analyze --litelement",
15
17
  "build": "tsc && npm run analyze -- --exclude dist",
18
+ "build:bundle": "esbuild dist/index.js --bundle --outfile=dist/bundle.js --format=esm --platform=browser",
19
+ "release": "npm run build && npm run build:bundle && npm publish --access public",
16
20
  "start": "npm run build && concurrently -k -r \"tsc --watch --preserveWatchOutput\" \"web-dev-server\"",
17
21
  "prepublish": "tsc && npm run analyze -- --exclude dist",
18
22
  "lint": "eslint --ext .ts,.tsx src --ignore-path .gitignore && prettier \"src/**/*.ts\" --check --ignore-path .gitignore",