@corti/dictation-web 0.0.0-test.562 → 0.0.0-test.571

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +164 -112
  2. package/dist/bundle.js +1416 -546
  3. package/dist/components/audio-visualiser.d.ts +3 -2
  4. package/dist/components/audio-visualiser.js +15 -12
  5. package/dist/components/audio-visualiser.js.map +1 -1
  6. package/dist/components/corti-dictation.d.ts +26 -13
  7. package/dist/components/corti-dictation.js +70 -21
  8. package/dist/components/corti-dictation.js.map +1 -1
  9. package/dist/components/device-selector.d.ts +6 -16
  10. package/dist/components/device-selector.js +27 -58
  11. package/dist/components/device-selector.js.map +1 -1
  12. package/dist/components/keybinding-selector.d.ts +14 -0
  13. package/dist/components/keybinding-selector.js +81 -0
  14. package/dist/components/keybinding-selector.js.map +1 -0
  15. package/dist/components/language-selector.d.ts +8 -17
  16. package/dist/components/language-selector.js +26 -52
  17. package/dist/components/language-selector.js.map +1 -1
  18. package/dist/components/mode-selector.d.ts +14 -0
  19. package/dist/components/mode-selector.js +73 -0
  20. package/dist/components/mode-selector.js.map +1 -0
  21. package/dist/components/recording-button.d.ts +8 -14
  22. package/dist/components/recording-button.js +155 -96
  23. package/dist/components/recording-button.js.map +1 -1
  24. package/dist/components/settings-menu.d.ts +4 -2
  25. package/dist/components/settings-menu.js +34 -14
  26. package/dist/components/settings-menu.js.map +1 -1
  27. package/dist/constants.d.ts +5 -0
  28. package/dist/constants.js +5 -0
  29. package/dist/constants.js.map +1 -1
  30. package/dist/contexts/dictation-context.d.ts +24 -14
  31. package/dist/contexts/dictation-context.js +128 -46
  32. package/dist/contexts/dictation-context.js.map +1 -1
  33. package/dist/controllers/devices-controller.d.ts +26 -0
  34. package/dist/controllers/devices-controller.js +99 -0
  35. package/dist/controllers/devices-controller.js.map +1 -0
  36. package/dist/controllers/dictation-controller.d.ts +29 -0
  37. package/dist/controllers/dictation-controller.js +179 -0
  38. package/dist/controllers/dictation-controller.js.map +1 -0
  39. package/dist/controllers/keybinding-controller.d.ts +17 -0
  40. package/dist/controllers/keybinding-controller.js +80 -0
  41. package/dist/controllers/keybinding-controller.js.map +1 -0
  42. package/dist/controllers/languages-controller.d.ts +26 -0
  43. package/dist/controllers/languages-controller.js +83 -0
  44. package/dist/controllers/languages-controller.js.map +1 -0
  45. package/dist/controllers/media-controller.d.ts +24 -0
  46. package/dist/controllers/media-controller.js +115 -0
  47. package/dist/controllers/media-controller.js.map +1 -0
  48. package/dist/index.d.ts +9 -7
  49. package/dist/index.js +30 -20
  50. package/dist/index.js.map +1 -1
  51. package/dist/src/components/audio-visualiser.d.ts +1 -0
  52. package/dist/src/components/audio-visualiser.js +6 -3
  53. package/dist/src/components/audio-visualiser.js.map +1 -1
  54. package/dist/src/components/corti-dictation.d.ts +21 -6
  55. package/dist/src/components/corti-dictation.js +25 -2
  56. package/dist/src/components/corti-dictation.js.map +1 -1
  57. package/dist/src/components/recording-button.d.ts +4 -0
  58. package/dist/src/components/recording-button.js +34 -10
  59. package/dist/src/components/recording-button.js.map +1 -1
  60. package/dist/src/components/settings-menu.js +1 -1
  61. package/dist/src/components/settings-menu.js.map +1 -1
  62. package/dist/src/constants.js +24 -6
  63. package/dist/src/constants.js.map +1 -1
  64. package/dist/src/contexts/dictation-context.d.ts +19 -5
  65. package/dist/src/contexts/dictation-context.js +32 -12
  66. package/dist/src/contexts/dictation-context.js.map +1 -1
  67. package/dist/src/controllers/DictationController.d.ts +5 -0
  68. package/dist/src/controllers/DictationController.js +27 -8
  69. package/dist/src/controllers/DictationController.js.map +1 -1
  70. package/dist/src/controllers/MediaController.d.ts +6 -0
  71. package/dist/src/controllers/MediaController.js +17 -1
  72. package/dist/src/controllers/MediaController.js.map +1 -1
  73. package/dist/src/styles/ComponentStyles.js +5 -5
  74. package/dist/src/styles/ComponentStyles.js.map +1 -1
  75. package/dist/src/styles/audio-visualiser.js +1 -1
  76. package/dist/src/styles/audio-visualiser.js.map +1 -1
  77. package/dist/src/styles/buttons.js +12 -12
  78. package/dist/src/styles/buttons.js.map +1 -1
  79. package/dist/src/styles/callout.js +7 -7
  80. package/dist/src/styles/callout.js.map +1 -1
  81. package/dist/src/styles/select.js +8 -8
  82. package/dist/src/styles/select.js.map +1 -1
  83. package/dist/src/styles/settings-menu.js +4 -4
  84. package/dist/src/styles/settings-menu.js.map +1 -1
  85. package/dist/src/types.d.ts +5 -0
  86. package/dist/src/types.js.map +1 -1
  87. package/dist/src/utils/converters.js +4 -1
  88. package/dist/src/utils/converters.js.map +1 -1
  89. package/dist/src/utils/events.d.ts +11 -4
  90. package/dist/src/utils/events.js.map +1 -1
  91. package/dist/src/utils/languages.js +2 -1
  92. package/dist/src/utils/languages.js.map +1 -1
  93. package/dist/src/utils/media.d.ts +1 -1
  94. package/dist/src/utils/media.js +13 -1
  95. package/dist/src/utils/media.js.map +1 -1
  96. package/dist/src/utils/token.d.ts +1 -1
  97. package/dist/src/utils/token.js +12 -11
  98. package/dist/src/utils/token.js.map +1 -1
  99. package/dist/styles/buttons.js +1 -2
  100. package/dist/styles/buttons.js.map +1 -1
  101. package/dist/styles/component-styles.d.ts +3 -0
  102. package/dist/styles/component-styles.js +32 -0
  103. package/dist/styles/component-styles.js.map +1 -0
  104. package/dist/styles/keybinding-selector.d.ts +2 -0
  105. package/dist/styles/keybinding-selector.js +72 -0
  106. package/dist/styles/keybinding-selector.js.map +1 -0
  107. package/dist/styles/mode-selector.d.ts +2 -0
  108. package/dist/styles/mode-selector.js +54 -0
  109. package/dist/styles/mode-selector.js.map +1 -0
  110. package/dist/styles/select.d.ts +1 -1
  111. package/dist/styles/select.js +6 -10
  112. package/dist/styles/select.js.map +1 -1
  113. package/dist/styles/settings-menu.js +9 -1
  114. package/dist/styles/settings-menu.js.map +1 -1
  115. package/dist/tsconfig.stories.tsbuildinfo +1 -1
  116. package/dist/types.d.ts +3 -1
  117. package/dist/types.js.map +1 -1
  118. package/dist/utils/events.d.ts +12 -3
  119. package/dist/utils/events.js +14 -0
  120. package/dist/utils/events.js.map +1 -1
  121. package/dist/utils/keybinding.d.ts +49 -0
  122. package/dist/utils/keybinding.js +140 -0
  123. package/dist/utils/keybinding.js.map +1 -0
  124. package/dist/utils/languages.d.ts +4 -3
  125. package/dist/utils/languages.js.map +1 -1
  126. package/dist/utils/token.d.ts +1 -1
  127. package/dist/utils/token.js +12 -11
  128. package/dist/utils/token.js.map +1 -1
  129. package/package.json +9 -8
@@ -0,0 +1,179 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _DictationController_instances, _DictationController_cortiClient, _DictationController_webSocket, _DictationController_closeTimeout, _DictationController_callbacks, _DictationController_lastDictationConfig, _DictationController_lastSocketUrl, _DictationController_lastSocketProxy, _DictationController_configHasChanged, _DictationController_connectProxy, _DictationController_connectAuth, _DictationController_setupWebSocketHandlers, _DictationController_setupMediaRecorder;
13
+ import { CortiClient, CortiWebSocketProxyClient } from "@corti/sdk";
14
+ import { DEFAULT_DICTATION_CONFIG } from "../constants.js";
15
+ export class DictationController {
16
+ constructor(host) {
17
+ _DictationController_instances.add(this);
18
+ _DictationController_cortiClient.set(this, null);
19
+ _DictationController_webSocket.set(this, null);
20
+ _DictationController_closeTimeout.set(this, void 0);
21
+ _DictationController_callbacks.set(this, void 0);
22
+ _DictationController_lastDictationConfig.set(this, null);
23
+ _DictationController_lastSocketUrl.set(this, void 0);
24
+ _DictationController_lastSocketProxy.set(this, void 0);
25
+ this.host = host;
26
+ host.addController(this);
27
+ }
28
+ hostDisconnected() {
29
+ this.cleanup();
30
+ }
31
+ async connect(mediaRecorder, dictationConfig = DEFAULT_DICTATION_CONFIG, callbacks = {}) {
32
+ if (!mediaRecorder) {
33
+ throw new Error("MediaRecorder is required to connect");
34
+ }
35
+ const newConnection = __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_configHasChanged).call(this) ||
36
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f")?.readyState !== WebSocket.OPEN;
37
+ if (newConnection) {
38
+ this.cleanup();
39
+ if (__classPrivateFieldGet(this, _DictationController_webSocket, "f")?.readyState === WebSocket.OPEN) {
40
+ throw new Error("Already connected. Disconnect before reconnecting.");
41
+ }
42
+ __classPrivateFieldSet(this, _DictationController_webSocket, this.host._socketUrl || this.host._socketProxy
43
+ ? await __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_connectProxy).call(this, dictationConfig)
44
+ : await __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_connectAuth).call(this, dictationConfig), "f");
45
+ __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", {
46
+ configuration: dictationConfig,
47
+ type: "config",
48
+ });
49
+ __classPrivateFieldSet(this, _DictationController_lastDictationConfig, this.host._dictationConfig || null, "f");
50
+ __classPrivateFieldSet(this, _DictationController_lastSocketUrl, this.host._socketUrl, "f");
51
+ __classPrivateFieldSet(this, _DictationController_lastSocketProxy, this.host._socketProxy, "f");
52
+ }
53
+ __classPrivateFieldSet(this, _DictationController_callbacks, callbacks, "f");
54
+ __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_setupMediaRecorder).call(this, mediaRecorder);
55
+ __classPrivateFieldGet(this, _DictationController_instances, "m", _DictationController_setupWebSocketHandlers).call(this, callbacks);
56
+ return newConnection;
57
+ }
58
+ async disconnect(onClose) {
59
+ await new Promise((resolve, reject) => {
60
+ if (!__classPrivateFieldGet(this, _DictationController_webSocket, "f") || __classPrivateFieldGet(this, _DictationController_webSocket, "f").readyState !== WebSocket.OPEN) {
61
+ resolve();
62
+ return;
63
+ }
64
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("close", (event) => {
65
+ if (__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")) {
66
+ clearTimeout(__classPrivateFieldGet(this, _DictationController_closeTimeout, "f"));
67
+ __classPrivateFieldSet(this, _DictationController_closeTimeout, undefined, "f");
68
+ }
69
+ if (onClose) {
70
+ onClose(event);
71
+ }
72
+ resolve();
73
+ });
74
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("message", (message) => {
75
+ __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("received", message);
76
+ if (__classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onMessage) {
77
+ __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onMessage(message);
78
+ }
79
+ if (message.type === "flushed") {
80
+ if (__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")) {
81
+ clearTimeout(__classPrivateFieldGet(this, _DictationController_closeTimeout, "f"));
82
+ __classPrivateFieldSet(this, _DictationController_closeTimeout, undefined, "f");
83
+ }
84
+ resolve();
85
+ return;
86
+ }
87
+ });
88
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").sendFlush({ type: "flush" });
89
+ __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", { type: "flush" });
90
+ __classPrivateFieldSet(this, _DictationController_closeTimeout, window.setTimeout(() => {
91
+ // Reject the promise before closing the web socket, so the promise rejects before close event fires
92
+ reject(new Error("Audio processing timeout"));
93
+ if (__classPrivateFieldGet(this, _DictationController_webSocket, "f")?.readyState === WebSocket.OPEN) {
94
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").close();
95
+ }
96
+ }, 10000), "f");
97
+ });
98
+ }
99
+ cleanup() {
100
+ if (__classPrivateFieldGet(this, _DictationController_closeTimeout, "f")) {
101
+ clearTimeout(__classPrivateFieldGet(this, _DictationController_closeTimeout, "f"));
102
+ __classPrivateFieldSet(this, _DictationController_closeTimeout, undefined, "f");
103
+ }
104
+ if (__classPrivateFieldGet(this, _DictationController_webSocket, "f")?.readyState === WebSocket.OPEN) {
105
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").close();
106
+ }
107
+ __classPrivateFieldSet(this, _DictationController_webSocket, null, "f");
108
+ __classPrivateFieldSet(this, _DictationController_cortiClient, null, "f");
109
+ __classPrivateFieldSet(this, _DictationController_lastDictationConfig, null, "f");
110
+ __classPrivateFieldSet(this, _DictationController_lastSocketUrl, undefined, "f");
111
+ __classPrivateFieldSet(this, _DictationController_lastSocketProxy, undefined, "f");
112
+ }
113
+ }
114
+ _DictationController_cortiClient = new WeakMap(), _DictationController_webSocket = new WeakMap(), _DictationController_closeTimeout = new WeakMap(), _DictationController_callbacks = new WeakMap(), _DictationController_lastDictationConfig = new WeakMap(), _DictationController_lastSocketUrl = new WeakMap(), _DictationController_lastSocketProxy = new WeakMap(), _DictationController_instances = new WeakSet(), _DictationController_configHasChanged = function _DictationController_configHasChanged() {
115
+ return (JSON.stringify(this.host._dictationConfig) !==
116
+ JSON.stringify(__classPrivateFieldGet(this, _DictationController_lastDictationConfig, "f")) ||
117
+ this.host._socketUrl !== __classPrivateFieldGet(this, _DictationController_lastSocketUrl, "f") ||
118
+ JSON.stringify(this.host._socketProxy) !==
119
+ JSON.stringify(__classPrivateFieldGet(this, _DictationController_lastSocketProxy, "f")));
120
+ }, _DictationController_connectProxy = async function _DictationController_connectProxy(dictationConfig) {
121
+ const proxyOptions = this.host._socketProxy || {
122
+ url: this.host._socketUrl || "",
123
+ };
124
+ if (!proxyOptions.url) {
125
+ throw new Error("Proxy URL is required when using proxy client");
126
+ }
127
+ return await CortiWebSocketProxyClient.transcribe.connect({
128
+ configuration: dictationConfig,
129
+ proxy: proxyOptions,
130
+ });
131
+ }, _DictationController_connectAuth = async function _DictationController_connectAuth(dictationConfig) {
132
+ if (!this.host._authConfig && !this.host._accessToken) {
133
+ throw new Error("Auth configuration or access token is required to connect");
134
+ }
135
+ // Use authConfig if available, otherwise create one from accessToken
136
+ const auth = this.host._authConfig || {
137
+ accessToken: this.host._accessToken || "",
138
+ refreshAccessToken: () => ({
139
+ accessToken: this.host._accessToken || "",
140
+ }),
141
+ };
142
+ __classPrivateFieldSet(this, _DictationController_cortiClient, new CortiClient({
143
+ auth,
144
+ environment: this.host._region,
145
+ tenantName: this.host._tenantName,
146
+ }), "f");
147
+ return await __classPrivateFieldGet(this, _DictationController_cortiClient, "f").transcribe.connect({
148
+ configuration: dictationConfig,
149
+ });
150
+ }, _DictationController_setupWebSocketHandlers = function _DictationController_setupWebSocketHandlers(callbacks) {
151
+ if (!__classPrivateFieldGet(this, _DictationController_webSocket, "f")) {
152
+ throw new Error("WebSocket not initialized");
153
+ }
154
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("message", (message) => {
155
+ callbacks.onNetworkActivity?.("received", message);
156
+ if (callbacks.onMessage) {
157
+ callbacks.onMessage(message);
158
+ }
159
+ });
160
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("error", (event) => {
161
+ if (callbacks.onError) {
162
+ callbacks.onError(event);
163
+ }
164
+ });
165
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f").on("close", (event) => {
166
+ if (callbacks.onClose) {
167
+ callbacks.onClose(event);
168
+ }
169
+ });
170
+ }, _DictationController_setupMediaRecorder = function _DictationController_setupMediaRecorder(mediaRecorder) {
171
+ mediaRecorder.ondataavailable = (event) => {
172
+ __classPrivateFieldGet(this, _DictationController_webSocket, "f")?.sendAudio(event.data);
173
+ __classPrivateFieldGet(this, _DictationController_callbacks, "f")?.onNetworkActivity?.("sent", {
174
+ size: event.data.size,
175
+ type: "audio",
176
+ });
177
+ };
178
+ };
179
+ //# sourceMappingURL=dictation-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dictation-controller.js","sourceRoot":"","sources":["../../src/controllers/dictation-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Corti, CortiClient, CortiWebSocketProxyClient } from \"@corti/sdk\";\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport { DEFAULT_DICTATION_CONFIG } from \"../constants.js\";\nimport type { ProxyOptions } from \"../types.js\";\n\ntype TranscribeSocket = Awaited<\n ReturnType<CortiClient[\"transcribe\"][\"connect\"]>\n>;\n\ninterface DictationControllerHost extends ReactiveControllerHost {\n _accessToken?: string;\n _authConfig?: Corti.BearerOptions;\n _region?: string;\n _tenantName?: string;\n _socketUrl?: string;\n _socketProxy?: ProxyOptions;\n _dictationConfig?: Corti.TranscribeConfig;\n}\n\nexport type TranscribeMessage =\n | Corti.TranscribeConfigStatusMessage\n | Corti.TranscribeUsageMessage\n | Corti.TranscribeEndedMessage\n | Corti.TranscribeErrorMessage\n | Corti.TranscribeTranscriptMessage\n | Corti.TranscribeCommandMessage\n | Corti.TranscribeFlushedMessage;\n\ninterface WebSocketCallbacks {\n onMessage?: (message: TranscribeMessage) => void;\n onError?: (error: Error) => void;\n onClose?: (event: unknown) => void;\n onNetworkActivity?: (direction: \"sent\" | \"received\", data: unknown) => void;\n}\n\nexport class DictationController implements ReactiveController {\n host: DictationControllerHost;\n\n #cortiClient: CortiClient | null = null;\n #webSocket: TranscribeSocket | null = null;\n #closeTimeout?: number;\n #callbacks?: WebSocketCallbacks;\n #lastDictationConfig: Corti.TranscribeConfig | null = null;\n #lastSocketUrl?: string;\n #lastSocketProxy?: ProxyOptions;\n\n constructor(host: DictationControllerHost) {\n this.host = host;\n host.addController(this);\n }\n\n hostDisconnected(): void {\n this.cleanup();\n }\n\n #configHasChanged(): boolean {\n return (\n JSON.stringify(this.host._dictationConfig) !==\n JSON.stringify(this.#lastDictationConfig) ||\n this.host._socketUrl !== this.#lastSocketUrl ||\n JSON.stringify(this.host._socketProxy) !==\n JSON.stringify(this.#lastSocketProxy)\n );\n }\n\n async connect(\n mediaRecorder: MediaRecorder | null,\n dictationConfig: Corti.TranscribeConfig = DEFAULT_DICTATION_CONFIG,\n callbacks: WebSocketCallbacks = {},\n ): Promise<boolean> {\n if (!mediaRecorder) {\n throw new Error(\"MediaRecorder is required to connect\");\n }\n\n const newConnection =\n this.#configHasChanged() ||\n this.#webSocket?.readyState !== WebSocket.OPEN;\n\n if (newConnection) {\n this.cleanup();\n\n if (this.#webSocket?.readyState === WebSocket.OPEN) {\n throw new Error(\"Already connected. Disconnect before reconnecting.\");\n }\n\n this.#webSocket =\n this.host._socketUrl || this.host._socketProxy\n ? await this.#connectProxy(dictationConfig)\n : await this.#connectAuth(dictationConfig);\n\n this.#callbacks?.onNetworkActivity?.(\"sent\", {\n configuration: dictationConfig,\n type: \"config\",\n });\n\n this.#lastDictationConfig = this.host._dictationConfig || null;\n this.#lastSocketUrl = this.host._socketUrl;\n this.#lastSocketProxy = this.host._socketProxy;\n }\n\n this.#callbacks = callbacks;\n this.#setupMediaRecorder(mediaRecorder);\n this.#setupWebSocketHandlers(callbacks);\n\n return newConnection;\n }\n\n async #connectProxy(\n dictationConfig: Corti.TranscribeConfig,\n ): Promise<TranscribeSocket> {\n const proxyOptions = this.host._socketProxy || {\n url: this.host._socketUrl || \"\",\n };\n\n if (!proxyOptions.url) {\n throw new Error(\"Proxy URL is required when using proxy client\");\n }\n\n return await CortiWebSocketProxyClient.transcribe.connect({\n configuration: dictationConfig,\n proxy: proxyOptions,\n });\n }\n\n async #connectAuth(\n dictationConfig: Corti.TranscribeConfig,\n ): Promise<TranscribeSocket> {\n if (!this.host._authConfig && !this.host._accessToken) {\n throw new Error(\n \"Auth configuration or access token is required to connect\",\n );\n }\n\n // Use authConfig if available, otherwise create one from accessToken\n const auth: Corti.BearerOptions = this.host._authConfig || {\n accessToken: this.host._accessToken || \"\",\n refreshAccessToken: () => ({\n accessToken: this.host._accessToken || \"\",\n }),\n };\n\n this.#cortiClient = new CortiClient({\n auth,\n environment: this.host._region,\n tenantName: this.host._tenantName,\n });\n\n return await this.#cortiClient.transcribe.connect({\n configuration: dictationConfig,\n });\n }\n\n #setupWebSocketHandlers(callbacks: WebSocketCallbacks): void {\n if (!this.#webSocket) {\n throw new Error(\"WebSocket not initialized\");\n }\n\n this.#webSocket.on(\"message\", (message: TranscribeMessage) => {\n callbacks.onNetworkActivity?.(\"received\", message);\n\n if (callbacks.onMessage) {\n callbacks.onMessage(message);\n }\n });\n\n this.#webSocket.on(\"error\", (event: Error) => {\n if (callbacks.onError) {\n callbacks.onError(event);\n }\n });\n\n this.#webSocket.on(\"close\", (event: unknown) => {\n if (callbacks.onClose) {\n callbacks.onClose(event);\n }\n });\n }\n\n #setupMediaRecorder(mediaRecorder: MediaRecorder): void {\n mediaRecorder.ondataavailable = (event) => {\n this.#webSocket?.sendAudio(event.data);\n this.#callbacks?.onNetworkActivity?.(\"sent\", {\n size: event.data.size,\n type: \"audio\",\n });\n };\n }\n\n async disconnect(onClose?: (event: unknown) => void): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n if (!this.#webSocket || this.#webSocket.readyState !== WebSocket.OPEN) {\n resolve();\n return;\n }\n\n this.#webSocket.on(\"close\", (event) => {\n if (this.#closeTimeout) {\n clearTimeout(this.#closeTimeout);\n this.#closeTimeout = undefined;\n }\n\n if (onClose) {\n onClose(event);\n }\n\n resolve();\n });\n\n this.#webSocket.on(\"message\", (message) => {\n this.#callbacks?.onNetworkActivity?.(\"received\", message);\n\n if (this.#callbacks?.onMessage) {\n this.#callbacks?.onMessage(message);\n }\n\n if (message.type === \"flushed\") {\n if (this.#closeTimeout) {\n clearTimeout(this.#closeTimeout);\n this.#closeTimeout = undefined;\n }\n\n resolve();\n return;\n }\n });\n\n this.#webSocket.sendFlush({ type: \"flush\" });\n this.#callbacks?.onNetworkActivity?.(\"sent\", { type: \"flush\" });\n\n this.#closeTimeout = window.setTimeout(() => {\n // Reject the promise before closing the web socket, so the promise rejects before close event fires\n reject(new Error(\"Audio processing timeout\"));\n\n if (this.#webSocket?.readyState === WebSocket.OPEN) {\n this.#webSocket.close();\n }\n }, 10000);\n });\n }\n\n cleanup(): void {\n if (this.#closeTimeout) {\n clearTimeout(this.#closeTimeout);\n this.#closeTimeout = undefined;\n }\n\n if (this.#webSocket?.readyState === WebSocket.OPEN) {\n this.#webSocket.close();\n }\n\n this.#webSocket = null;\n this.#cortiClient = null;\n this.#lastDictationConfig = null;\n this.#lastSocketUrl = undefined;\n this.#lastSocketProxy = undefined;\n }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from "lit";
2
+ import type { DictationMode } from "../types.js";
3
+ interface KeybindingControllerHost extends ReactiveControllerHost {
4
+ _keybinding?: string | null;
5
+ _mode?: DictationMode;
6
+ startRecording(): void;
7
+ stopRecording(): void;
8
+ toggleRecording(): void;
9
+ }
10
+ export declare class KeybindingController implements ReactiveController {
11
+ #private;
12
+ host: KeybindingControllerHost;
13
+ constructor(host: KeybindingControllerHost);
14
+ hostConnected(): void;
15
+ hostDisconnected(): void;
16
+ }
17
+ export {};
@@ -0,0 +1,80 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _KeybindingController_instances, _KeybindingController_keydownHandler, _KeybindingController_keyupHandler, _KeybindingController_blurHandler, _KeybindingController_setupListeners, _KeybindingController_removeListeners;
13
+ import { matchesKeybinding, shouldIgnoreKeybinding, } from "../utils/keybinding.js";
14
+ export class KeybindingController {
15
+ constructor(host) {
16
+ _KeybindingController_instances.add(this);
17
+ _KeybindingController_keydownHandler.set(this, void 0);
18
+ _KeybindingController_keyupHandler.set(this, void 0);
19
+ _KeybindingController_blurHandler.set(this, void 0);
20
+ this.host = host;
21
+ host.addController(this);
22
+ }
23
+ hostConnected() {
24
+ __classPrivateFieldGet(this, _KeybindingController_instances, "m", _KeybindingController_setupListeners).call(this);
25
+ }
26
+ hostDisconnected() {
27
+ __classPrivateFieldGet(this, _KeybindingController_instances, "m", _KeybindingController_removeListeners).call(this);
28
+ }
29
+ }
30
+ _KeybindingController_keydownHandler = new WeakMap(), _KeybindingController_keyupHandler = new WeakMap(), _KeybindingController_blurHandler = new WeakMap(), _KeybindingController_instances = new WeakSet(), _KeybindingController_setupListeners = function _KeybindingController_setupListeners() {
31
+ __classPrivateFieldGet(this, _KeybindingController_instances, "m", _KeybindingController_removeListeners).call(this);
32
+ __classPrivateFieldSet(this, _KeybindingController_keydownHandler, (event) => {
33
+ if (!this.host._keybinding) {
34
+ return;
35
+ }
36
+ if (shouldIgnoreKeybinding(document.activeElement)) {
37
+ return;
38
+ }
39
+ if (matchesKeybinding(event, this.host._keybinding)) {
40
+ event.preventDefault();
41
+ if (this.host._mode === "push-to-talk") {
42
+ this.host.startRecording();
43
+ }
44
+ if (this.host._mode === "toggle-to-talk") {
45
+ this.host.toggleRecording();
46
+ }
47
+ }
48
+ }, "f");
49
+ __classPrivateFieldSet(this, _KeybindingController_keyupHandler, (event) => {
50
+ if (!this.host._keybinding) {
51
+ return;
52
+ }
53
+ if (this.host._mode === "push-to-talk" &&
54
+ matchesKeybinding(event, this.host._keybinding)) {
55
+ this.host.stopRecording();
56
+ }
57
+ }, "f");
58
+ __classPrivateFieldSet(this, _KeybindingController_blurHandler, () => {
59
+ if (this.host._mode === "push-to-talk") {
60
+ this.host.stopRecording();
61
+ }
62
+ }, "f");
63
+ window.addEventListener("keydown", __classPrivateFieldGet(this, _KeybindingController_keydownHandler, "f"));
64
+ window.addEventListener("keyup", __classPrivateFieldGet(this, _KeybindingController_keyupHandler, "f"));
65
+ window.addEventListener("blur", __classPrivateFieldGet(this, _KeybindingController_blurHandler, "f"));
66
+ }, _KeybindingController_removeListeners = function _KeybindingController_removeListeners() {
67
+ if (__classPrivateFieldGet(this, _KeybindingController_keydownHandler, "f")) {
68
+ window.removeEventListener("keydown", __classPrivateFieldGet(this, _KeybindingController_keydownHandler, "f"));
69
+ __classPrivateFieldSet(this, _KeybindingController_keydownHandler, undefined, "f");
70
+ }
71
+ if (__classPrivateFieldGet(this, _KeybindingController_keyupHandler, "f")) {
72
+ window.removeEventListener("keyup", __classPrivateFieldGet(this, _KeybindingController_keyupHandler, "f"));
73
+ __classPrivateFieldSet(this, _KeybindingController_keyupHandler, undefined, "f");
74
+ }
75
+ if (__classPrivateFieldGet(this, _KeybindingController_blurHandler, "f")) {
76
+ window.removeEventListener("blur", __classPrivateFieldGet(this, _KeybindingController_blurHandler, "f"));
77
+ __classPrivateFieldSet(this, _KeybindingController_blurHandler, undefined, "f");
78
+ }
79
+ };
80
+ //# sourceMappingURL=keybinding-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keybinding-controller.js","sourceRoot":"","sources":["../../src/controllers/keybinding-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport type { DictationMode } from \"../types.js\";\nimport {\n matchesKeybinding,\n shouldIgnoreKeybinding,\n} from \"../utils/keybinding.js\";\n\ninterface KeybindingControllerHost extends ReactiveControllerHost {\n _keybinding?: string | null;\n _mode?: DictationMode;\n startRecording(): void;\n stopRecording(): void;\n toggleRecording(): void;\n}\n\nexport class KeybindingController implements ReactiveController {\n host: KeybindingControllerHost;\n\n #keydownHandler?: (event: KeyboardEvent) => void;\n #keyupHandler?: (event: KeyboardEvent) => void;\n #blurHandler?: () => void;\n\n constructor(host: KeybindingControllerHost) {\n this.host = host;\n host.addController(this);\n }\n\n hostConnected(): void {\n this.#setupListeners();\n }\n\n hostDisconnected(): void {\n this.#removeListeners();\n }\n\n #setupListeners(): void {\n this.#removeListeners();\n\n this.#keydownHandler = (event: KeyboardEvent) => {\n if (!this.host._keybinding) {\n return;\n }\n\n if (shouldIgnoreKeybinding(document.activeElement)) {\n return;\n }\n\n if (matchesKeybinding(event, this.host._keybinding)) {\n event.preventDefault();\n\n if (this.host._mode === \"push-to-talk\") {\n this.host.startRecording();\n }\n\n if (this.host._mode === \"toggle-to-talk\") {\n this.host.toggleRecording();\n }\n }\n };\n\n this.#keyupHandler = (event: KeyboardEvent) => {\n if (!this.host._keybinding) {\n return;\n }\n\n if (\n this.host._mode === \"push-to-talk\" &&\n matchesKeybinding(event, this.host._keybinding)\n ) {\n this.host.stopRecording();\n }\n };\n\n this.#blurHandler = () => {\n if (this.host._mode === \"push-to-talk\") {\n this.host.stopRecording();\n }\n };\n\n window.addEventListener(\"keydown\", this.#keydownHandler);\n window.addEventListener(\"keyup\", this.#keyupHandler);\n window.addEventListener(\"blur\", this.#blurHandler);\n }\n\n #removeListeners(): void {\n if (this.#keydownHandler) {\n window.removeEventListener(\"keydown\", this.#keydownHandler);\n this.#keydownHandler = undefined;\n }\n\n if (this.#keyupHandler) {\n window.removeEventListener(\"keyup\", this.#keyupHandler);\n this.#keyupHandler = undefined;\n }\n\n if (this.#blurHandler) {\n window.removeEventListener(\"blur\", this.#blurHandler);\n this.#blurHandler = undefined;\n }\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { Corti } from "@corti/sdk";
2
+ import type { ReactiveController, ReactiveControllerHost } from "lit";
3
+ interface LanguagesControllerHost extends ReactiveControllerHost {
4
+ region?: string;
5
+ dictationConfig?: Corti.TranscribeConfig;
6
+ dispatchEvent(event: CustomEvent): boolean;
7
+ requestUpdate(): void;
8
+ _languages?: Corti.TranscribeSupportedLanguage[];
9
+ }
10
+ /**
11
+ * Controller that manages automatic language loading based on region.
12
+ * Loads languages when they're not present and handles region changes.
13
+ * Reacts to updates and automatically loads languages when needed.
14
+ */
15
+ export declare class LanguagesController implements ReactiveController {
16
+ #private;
17
+ host: LanguagesControllerHost;
18
+ constructor(host: LanguagesControllerHost);
19
+ initialize(): void;
20
+ hostUpdate(): void;
21
+ /**
22
+ * Clear the auto-loaded flag (when languages are set externally)
23
+ */
24
+ clearAutoLoadedFlag(): void;
25
+ }
26
+ export {};
@@ -0,0 +1,83 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _LanguagesController_instances, _LanguagesController_autoLoadedLanguages, _LanguagesController_loadingLanguages, _LanguagesController_previousRegion, _LanguagesController_initialized, _LanguagesController_loadLanguages;
13
+ import { errorEvent, languagesChangedEvent } from "../utils/events.js";
14
+ import { getLanguagesByRegion } from "../utils/languages.js";
15
+ /**
16
+ * Controller that manages automatic language loading based on region.
17
+ * Loads languages when they're not present and handles region changes.
18
+ * Reacts to updates and automatically loads languages when needed.
19
+ */
20
+ export class LanguagesController {
21
+ constructor(host) {
22
+ _LanguagesController_instances.add(this);
23
+ _LanguagesController_autoLoadedLanguages.set(this, false);
24
+ _LanguagesController_loadingLanguages.set(this, false);
25
+ _LanguagesController_previousRegion.set(this, void 0);
26
+ _LanguagesController_initialized.set(this, false);
27
+ this.host = host;
28
+ host.addController(this);
29
+ }
30
+ initialize() {
31
+ __classPrivateFieldSet(this, _LanguagesController_initialized, true, "f");
32
+ if (this.host._languages === undefined) {
33
+ __classPrivateFieldGet(this, _LanguagesController_instances, "m", _LanguagesController_loadLanguages).call(this);
34
+ }
35
+ }
36
+ hostUpdate() {
37
+ // Only react to updates after initialization
38
+ if (!__classPrivateFieldGet(this, _LanguagesController_initialized, "f")) {
39
+ return;
40
+ }
41
+ // When region changes, reload languages if they were auto-loaded
42
+ if ((__classPrivateFieldGet(this, _LanguagesController_previousRegion, "f") !== this.host.region &&
43
+ __classPrivateFieldGet(this, _LanguagesController_autoLoadedLanguages, "f")) ||
44
+ this.host._languages === undefined) {
45
+ __classPrivateFieldGet(this, _LanguagesController_instances, "m", _LanguagesController_loadLanguages).call(this);
46
+ }
47
+ __classPrivateFieldSet(this, _LanguagesController_previousRegion, this.host.region, "f");
48
+ }
49
+ /**
50
+ * Clear the auto-loaded flag (when languages are set externally)
51
+ */
52
+ clearAutoLoadedFlag() {
53
+ __classPrivateFieldSet(this, _LanguagesController_autoLoadedLanguages, false, "f");
54
+ }
55
+ }
56
+ _LanguagesController_autoLoadedLanguages = new WeakMap(), _LanguagesController_loadingLanguages = new WeakMap(), _LanguagesController_previousRegion = new WeakMap(), _LanguagesController_initialized = new WeakMap(), _LanguagesController_instances = new WeakSet(), _LanguagesController_loadLanguages = async function _LanguagesController_loadLanguages() {
57
+ if (__classPrivateFieldGet(this, _LanguagesController_loadingLanguages, "f")) {
58
+ return;
59
+ }
60
+ __classPrivateFieldSet(this, _LanguagesController_loadingLanguages, true, "f");
61
+ try {
62
+ const { languages, defaultLanguage } = getLanguagesByRegion(this.host.region);
63
+ __classPrivateFieldSet(this, _LanguagesController_autoLoadedLanguages, true, "f");
64
+ this.host._languages = languages;
65
+ const previousLanguage = this.host.dictationConfig?.primaryLanguage;
66
+ const selectedLanguage = previousLanguage && languages.includes(previousLanguage)
67
+ ? previousLanguage
68
+ : defaultLanguage;
69
+ this.host.dictationConfig = {
70
+ ...this.host.dictationConfig,
71
+ primaryLanguage: selectedLanguage || "en",
72
+ };
73
+ this.host.requestUpdate();
74
+ this.host.dispatchEvent(languagesChangedEvent(languages, selectedLanguage));
75
+ }
76
+ catch (error) {
77
+ this.host.dispatchEvent(errorEvent(error));
78
+ }
79
+ finally {
80
+ __classPrivateFieldSet(this, _LanguagesController_loadingLanguages, false, "f");
81
+ }
82
+ };
83
+ //# sourceMappingURL=languages-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"languages-controller.js","sourceRoot":"","sources":["../../src/controllers/languages-controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAU7D;;;;GAIG;AACH,MAAM,OAAO,mBAAmB;IAO9B,YAAY,IAA6B;;QALzC,mDAAgC,KAAK,EAAC;QACtC,gDAA6B,KAAK,EAAC;QACnC,sDAAyB;QACzB,2CAAwB,KAAK,EAAC;QAG5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,UAAU;QACR,uBAAA,IAAI,oCAAgB,IAAI,MAAA,CAAC;QAEzB,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACvC,uBAAA,IAAI,0EAAe,MAAnB,IAAI,CAAiB,CAAC;QACxB,CAAC;IACH,CAAC;IAED,UAAU;QACR,6CAA6C;QAC7C,IAAI,CAAC,uBAAA,IAAI,wCAAa,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,iEAAiE;QACjE,IACE,CAAC,uBAAA,IAAI,2CAAgB,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM;YACxC,uBAAA,IAAI,gDAAqB,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,EAClC,CAAC;YACD,uBAAA,IAAI,0EAAe,MAAnB,IAAI,CAAiB,CAAC;QACxB,CAAC;QAED,uBAAA,IAAI,uCAAmB,IAAI,CAAC,IAAI,CAAC,MAAM,MAAA,CAAC;IAC1C,CAAC;IAuCD;;OAEG;IACH,mBAAmB;QACjB,uBAAA,IAAI,4CAAwB,KAAK,MAAA,CAAC;IACpC,CAAC;CACF;6SA3CC,KAAK;IACH,IAAI,uBAAA,IAAI,6CAAkB,EAAE,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,uBAAA,IAAI,yCAAqB,IAAI,MAAA,CAAC;IAE9B,IAAI,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,oBAAoB,CACzD,IAAI,CAAC,IAAI,CAAC,MAAM,CACjB,CAAC;QAEF,uBAAA,IAAI,4CAAwB,IAAI,MAAA,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAEjC,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,eAAe,CAAC;QACpE,MAAM,gBAAgB,GACpB,gBAAgB,IAAI,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACtD,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,eAAe,CAAC;QAEtB,IAAI,CAAC,IAAI,CAAC,eAAe,GAAG;YAC1B,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe;YAC5B,eAAe,EAAE,gBAAgB,IAAI,IAAI;SAC1C,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,aAAa,CACrB,qBAAqB,CAAC,SAAS,EAAE,gBAAgB,CAAC,CACnD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7C,CAAC;YAAS,CAAC;QACT,uBAAA,IAAI,yCAAqB,KAAK,MAAA,CAAC;IACjC,CAAC;AACH,CAAC","sourcesContent":["import type { Corti } from \"@corti/sdk\";\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport { errorEvent, languagesChangedEvent } from \"../utils/events.js\";\nimport { getLanguagesByRegion } from \"../utils/languages.js\";\n\ninterface LanguagesControllerHost extends ReactiveControllerHost {\n region?: string;\n dictationConfig?: Corti.TranscribeConfig;\n dispatchEvent(event: CustomEvent): boolean;\n requestUpdate(): void;\n _languages?: Corti.TranscribeSupportedLanguage[];\n}\n\n/**\n * Controller that manages automatic language loading based on region.\n * Loads languages when they're not present and handles region changes.\n * Reacts to updates and automatically loads languages when needed.\n */\nexport class LanguagesController implements ReactiveController {\n host: LanguagesControllerHost;\n #autoLoadedLanguages: boolean = false;\n #loadingLanguages: boolean = false;\n #previousRegion?: string;\n #initialized: boolean = false;\n\n constructor(host: LanguagesControllerHost) {\n this.host = host;\n host.addController(this);\n }\n\n initialize(): void {\n this.#initialized = true;\n\n if (this.host._languages === undefined) {\n this.#loadLanguages();\n }\n }\n\n hostUpdate(): void {\n // Only react to updates after initialization\n if (!this.#initialized) {\n return;\n }\n\n // When region changes, reload languages if they were auto-loaded\n if (\n (this.#previousRegion !== this.host.region &&\n this.#autoLoadedLanguages) ||\n this.host._languages === undefined\n ) {\n this.#loadLanguages();\n }\n\n this.#previousRegion = this.host.region;\n }\n\n async #loadLanguages(): Promise<void> {\n if (this.#loadingLanguages) {\n return;\n }\n\n this.#loadingLanguages = true;\n\n try {\n const { languages, defaultLanguage } = getLanguagesByRegion(\n this.host.region,\n );\n\n this.#autoLoadedLanguages = true;\n this.host._languages = languages;\n\n const previousLanguage = this.host.dictationConfig?.primaryLanguage;\n const selectedLanguage =\n previousLanguage && languages.includes(previousLanguage)\n ? previousLanguage\n : defaultLanguage;\n\n this.host.dictationConfig = {\n ...this.host.dictationConfig,\n primaryLanguage: selectedLanguage || \"en\",\n };\n\n this.host.requestUpdate();\n this.host.dispatchEvent(\n languagesChangedEvent(languages, selectedLanguage),\n );\n } catch (error) {\n this.host.dispatchEvent(errorEvent(error));\n } finally {\n this.#loadingLanguages = false;\n }\n }\n\n /**\n * Clear the auto-loaded flag (when languages are set externally)\n */\n clearAutoLoadedFlag(): void {\n this.#autoLoadedLanguages = false;\n }\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from "lit";
2
+ interface MediaControllerHost extends ReactiveControllerHost {
3
+ _selectedDevice?: MediaDeviceInfo;
4
+ _debug_displayAudio?: boolean;
5
+ }
6
+ export declare class MediaController implements ReactiveController {
7
+ #private;
8
+ host: MediaControllerHost;
9
+ constructor(host: MediaControllerHost);
10
+ hostDisconnected(): void;
11
+ initialize(onTrackEnded?: () => void): Promise<void>;
12
+ getAudioLevel(): number;
13
+ startAudioLevelMonitoring(onAudioLevelChange?: (level: number) => void): void;
14
+ stopAudioLevelMonitoring(): void;
15
+ cleanup(): Promise<void>;
16
+ /**
17
+ * Stops the media recorder and waits for all buffered data to be flushed.
18
+ * This ensures the final ondataavailable event fires before resolving.
19
+ */
20
+ stopRecording(): Promise<void>;
21
+ get mediaRecorder(): MediaRecorder | null;
22
+ get audioLevel(): number;
23
+ }
24
+ export {};
@@ -0,0 +1,115 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _MediaController_mediaStream, _MediaController_audioContext, _MediaController_analyser, _MediaController_mediaRecorder, _MediaController_visualiserInterval, _MediaController_audioLevel, _MediaController_onTrackEnded, _MediaController_onAudioLevelChange;
13
+ import { calculateAudioLevel, createAudioAnalyzer, getMediaStream, } from "../utils/media.js";
14
+ export class MediaController {
15
+ constructor(host) {
16
+ _MediaController_mediaStream.set(this, null);
17
+ _MediaController_audioContext.set(this, null);
18
+ _MediaController_analyser.set(this, null);
19
+ _MediaController_mediaRecorder.set(this, null);
20
+ _MediaController_visualiserInterval.set(this, void 0);
21
+ _MediaController_audioLevel.set(this, 0);
22
+ _MediaController_onTrackEnded.set(this, void 0);
23
+ _MediaController_onAudioLevelChange.set(this, void 0);
24
+ this.host = host;
25
+ host.addController(this);
26
+ }
27
+ hostDisconnected() {
28
+ this.cleanup();
29
+ }
30
+ async initialize(onTrackEnded) {
31
+ await this.cleanup();
32
+ __classPrivateFieldSet(this, _MediaController_onTrackEnded, onTrackEnded, "f");
33
+ __classPrivateFieldSet(this, _MediaController_mediaStream, await getMediaStream(this.host._selectedDevice?.deviceId, this.host._debug_displayAudio), "f");
34
+ __classPrivateFieldGet(this, _MediaController_mediaStream, "f").getTracks().forEach((track) => {
35
+ track.addEventListener("ended", () => {
36
+ if (__classPrivateFieldGet(this, _MediaController_onTrackEnded, "f")) {
37
+ __classPrivateFieldGet(this, _MediaController_onTrackEnded, "f").call(this);
38
+ }
39
+ });
40
+ });
41
+ const { audioContext, analyser } = createAudioAnalyzer(__classPrivateFieldGet(this, _MediaController_mediaStream, "f"));
42
+ __classPrivateFieldSet(this, _MediaController_audioContext, audioContext, "f");
43
+ __classPrivateFieldSet(this, _MediaController_analyser, analyser, "f");
44
+ __classPrivateFieldSet(this, _MediaController_mediaRecorder, new MediaRecorder(__classPrivateFieldGet(this, _MediaController_mediaStream, "f")), "f");
45
+ }
46
+ getAudioLevel() {
47
+ return __classPrivateFieldGet(this, _MediaController_analyser, "f") ? calculateAudioLevel(__classPrivateFieldGet(this, _MediaController_analyser, "f")) : 0;
48
+ }
49
+ startAudioLevelMonitoring(onAudioLevelChange) {
50
+ this.stopAudioLevelMonitoring();
51
+ __classPrivateFieldSet(this, _MediaController_onAudioLevelChange, onAudioLevelChange, "f");
52
+ __classPrivateFieldSet(this, _MediaController_visualiserInterval, window.setInterval(() => {
53
+ __classPrivateFieldSet(this, _MediaController_audioLevel, this.getAudioLevel() * 3, "f");
54
+ this.host.requestUpdate();
55
+ if (__classPrivateFieldGet(this, _MediaController_onAudioLevelChange, "f")) {
56
+ __classPrivateFieldGet(this, _MediaController_onAudioLevelChange, "f").call(this, __classPrivateFieldGet(this, _MediaController_audioLevel, "f"));
57
+ }
58
+ }, 150), "f");
59
+ }
60
+ stopAudioLevelMonitoring() {
61
+ if (__classPrivateFieldGet(this, _MediaController_visualiserInterval, "f")) {
62
+ clearInterval(__classPrivateFieldGet(this, _MediaController_visualiserInterval, "f"));
63
+ __classPrivateFieldSet(this, _MediaController_visualiserInterval, undefined, "f");
64
+ }
65
+ __classPrivateFieldSet(this, _MediaController_audioLevel, 0, "f");
66
+ this.host.requestUpdate();
67
+ if (__classPrivateFieldGet(this, _MediaController_onAudioLevelChange, "f")) {
68
+ __classPrivateFieldGet(this, _MediaController_onAudioLevelChange, "f").call(this, __classPrivateFieldGet(this, _MediaController_audioLevel, "f"));
69
+ }
70
+ }
71
+ async cleanup() {
72
+ this.stopAudioLevelMonitoring();
73
+ if (__classPrivateFieldGet(this, _MediaController_mediaRecorder, "f")?.state === "recording") {
74
+ __classPrivateFieldGet(this, _MediaController_mediaRecorder, "f").stop();
75
+ }
76
+ if (__classPrivateFieldGet(this, _MediaController_mediaStream, "f")) {
77
+ __classPrivateFieldGet(this, _MediaController_mediaStream, "f").getTracks().forEach((track) => {
78
+ track.stop();
79
+ });
80
+ __classPrivateFieldSet(this, _MediaController_mediaStream, null, "f");
81
+ }
82
+ if (__classPrivateFieldGet(this, _MediaController_audioContext, "f") && __classPrivateFieldGet(this, _MediaController_audioContext, "f").state !== "closed") {
83
+ await __classPrivateFieldGet(this, _MediaController_audioContext, "f").close();
84
+ }
85
+ __classPrivateFieldSet(this, _MediaController_audioContext, null, "f");
86
+ __classPrivateFieldSet(this, _MediaController_analyser, null, "f");
87
+ __classPrivateFieldSet(this, _MediaController_mediaRecorder, null, "f");
88
+ __classPrivateFieldSet(this, _MediaController_onTrackEnded, undefined, "f");
89
+ __classPrivateFieldSet(this, _MediaController_onAudioLevelChange, undefined, "f");
90
+ }
91
+ /**
92
+ * Stops the media recorder and waits for all buffered data to be flushed.
93
+ * This ensures the final ondataavailable event fires before resolving.
94
+ */
95
+ async stopRecording() {
96
+ return new Promise((resolve) => {
97
+ if (!__classPrivateFieldGet(this, _MediaController_mediaRecorder, "f") || __classPrivateFieldGet(this, _MediaController_mediaRecorder, "f").state !== "recording") {
98
+ resolve();
99
+ return;
100
+ }
101
+ __classPrivateFieldGet(this, _MediaController_mediaRecorder, "f").onstop = () => {
102
+ resolve();
103
+ };
104
+ __classPrivateFieldGet(this, _MediaController_mediaRecorder, "f").stop();
105
+ });
106
+ }
107
+ get mediaRecorder() {
108
+ return __classPrivateFieldGet(this, _MediaController_mediaRecorder, "f");
109
+ }
110
+ get audioLevel() {
111
+ return __classPrivateFieldGet(this, _MediaController_audioLevel, "f");
112
+ }
113
+ }
114
+ _MediaController_mediaStream = new WeakMap(), _MediaController_audioContext = new WeakMap(), _MediaController_analyser = new WeakMap(), _MediaController_mediaRecorder = new WeakMap(), _MediaController_visualiserInterval = new WeakMap(), _MediaController_audioLevel = new WeakMap(), _MediaController_onTrackEnded = new WeakMap(), _MediaController_onAudioLevelChange = new WeakMap();
115
+ //# sourceMappingURL=media-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-controller.js","sourceRoot":"","sources":["../../src/controllers/media-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport {\n calculateAudioLevel,\n createAudioAnalyzer,\n getMediaStream,\n} from \"../utils/media.js\";\n\ninterface MediaControllerHost extends ReactiveControllerHost {\n _selectedDevice?: MediaDeviceInfo;\n _debug_displayAudio?: boolean;\n}\n\nexport class MediaController implements ReactiveController {\n host: MediaControllerHost;\n\n #mediaStream: MediaStream | null = null;\n #audioContext: AudioContext | null = null;\n #analyser: AnalyserNode | null = null;\n #mediaRecorder: MediaRecorder | null = null;\n #visualiserInterval?: number;\n #audioLevel: number = 0;\n #onTrackEnded?: () => void;\n #onAudioLevelChange?: (level: number) => void;\n\n constructor(host: MediaControllerHost) {\n this.host = host;\n host.addController(this);\n }\n\n hostDisconnected(): void {\n this.cleanup();\n }\n\n async initialize(onTrackEnded?: () => void): Promise<void> {\n await this.cleanup();\n\n this.#onTrackEnded = onTrackEnded;\n this.#mediaStream = await getMediaStream(\n this.host._selectedDevice?.deviceId,\n this.host._debug_displayAudio,\n );\n\n this.#mediaStream.getTracks().forEach((track: MediaStreamTrack) => {\n track.addEventListener(\"ended\", () => {\n if (this.#onTrackEnded) {\n this.#onTrackEnded();\n }\n });\n });\n\n const { audioContext, analyser } = createAudioAnalyzer(this.#mediaStream);\n\n this.#audioContext = audioContext;\n this.#analyser = analyser;\n\n this.#mediaRecorder = new MediaRecorder(this.#mediaStream);\n }\n\n getAudioLevel(): number {\n return this.#analyser ? calculateAudioLevel(this.#analyser) : 0;\n }\n\n startAudioLevelMonitoring(\n onAudioLevelChange?: (level: number) => void,\n ): void {\n this.stopAudioLevelMonitoring();\n\n this.#onAudioLevelChange = onAudioLevelChange;\n\n this.#visualiserInterval = window.setInterval(() => {\n this.#audioLevel = this.getAudioLevel() * 3;\n this.host.requestUpdate();\n\n if (this.#onAudioLevelChange) {\n this.#onAudioLevelChange(this.#audioLevel);\n }\n }, 150);\n }\n\n stopAudioLevelMonitoring(): void {\n if (this.#visualiserInterval) {\n clearInterval(this.#visualiserInterval);\n this.#visualiserInterval = undefined;\n }\n\n this.#audioLevel = 0;\n this.host.requestUpdate();\n\n if (this.#onAudioLevelChange) {\n this.#onAudioLevelChange(this.#audioLevel);\n }\n }\n\n async cleanup(): Promise<void> {\n this.stopAudioLevelMonitoring();\n\n if (this.#mediaRecorder?.state === \"recording\") {\n this.#mediaRecorder.stop();\n }\n\n if (this.#mediaStream) {\n this.#mediaStream.getTracks().forEach((track) => {\n track.stop();\n });\n this.#mediaStream = null;\n }\n\n if (this.#audioContext && this.#audioContext.state !== \"closed\") {\n await this.#audioContext.close();\n }\n\n this.#audioContext = null;\n\n this.#analyser = null;\n this.#mediaRecorder = null;\n this.#onTrackEnded = undefined;\n this.#onAudioLevelChange = undefined;\n }\n\n /**\n * Stops the media recorder and waits for all buffered data to be flushed.\n * This ensures the final ondataavailable event fires before resolving.\n */\n async stopRecording(): Promise<void> {\n return new Promise<void>((resolve) => {\n if (!this.#mediaRecorder || this.#mediaRecorder.state !== \"recording\") {\n resolve();\n return;\n }\n\n this.#mediaRecorder.onstop = () => {\n resolve();\n };\n\n this.#mediaRecorder.stop();\n });\n }\n\n get mediaRecorder(): MediaRecorder | null {\n return this.#mediaRecorder;\n }\n\n get audioLevel(): number {\n return this.#audioLevel;\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { CortiDictation } from "./components/corti-dictation.js";
2
2
  export { CortiDictation } from "./components/corti-dictation.js";
3
- export { DeviceSelector } from "./components/device-selector.js";
4
- export { LanguageSelector } from "./components/language-selector.js";
5
- export { RecordingButton } from "./components/recording-button.js";
6
- export { SettingsMenu } from "./components/settings-menu.js";
7
- export { DictationContext } from "./contexts/dictation-context.js";
8
- export type { ConfigurableSettings, RecordingState } from "./types.js";
9
- export type { AudioLevelChangedEventDetail, CommandEventDetail, ErrorEventDetail, LanguageChangedEventDetail, LanguagesChangedEventDetail, NetworkActivityEventDetail, RecordingDevicesChangedEventDetail, RecordingStateChangedEventDetail, TranscriptEventDetail, UsageEventDetail, } from "./utils/events.js";
3
+ export { DictationDeviceSelector } from "./components/device-selector.js";
4
+ export { DictationKeybindingSelector } from "./components/keybinding-selector.js";
5
+ export { DictationLanguageSelector } from "./components/language-selector.js";
6
+ export { DictationModeSelector } from "./components/mode-selector.js";
7
+ export { DictationRecordingButton } from "./components/recording-button.js";
8
+ export { DictationSettingsMenu } from "./components/settings-menu.js";
9
+ export { DictationRoot } from "./contexts/dictation-context.js";
10
+ export type { ConfigurableSettings, DictationMode, Keybinding, RecordingState, } from "./types.js";
11
+ export type { AudioLevelChangedEventDetail, CommandEventDetail, ErrorEventDetail, KeybindingChangedEventDetail, LanguageChangedEventDetail, LanguagesChangedEventDetail, ModeChangedEventDetail, NetworkActivityEventDetail, RecordingDevicesChangedEventDetail, RecordingStateChangedEventDetail, TranscriptEventDetail, UsageEventDetail, } from "./utils/events.js";
10
12
  export default CortiDictation;