@elizaos/capacitor-swabble 1.0.0
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/ElizaosCapacitorSwabble.podspec +18 -0
- package/android/build.gradle +50 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/ai/eliza/plugins/swabble/SwabblePlugin.kt +840 -0
- package/dist/esm/definitions.d.ts +218 -0
- package/dist/esm/definitions.d.ts.map +1 -0
- package/dist/esm/definitions.js +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/web.d.ts +54 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +461 -0
- package/dist/plugin.cjs.js +477 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +480 -0
- package/dist/plugin.js.map +1 -0
- package/electrobun/src/global.d.ts +1 -0
- package/electrobun/src/index.ts +786 -0
- package/electrobun/tsconfig.json +16 -0
- package/ios/Sources/SwabblePlugin/SwabblePlugin.swift +1156 -0
- package/package.json +84 -0
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
var capacitorSwabble = (function (exports, core) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const loadWeb = () => Promise.resolve().then(function () { return web; }).then((m) => new m.SwabbleWeb());
|
|
5
|
+
const Swabble = core.registerPlugin("Swabble", {
|
|
6
|
+
web: loadWeb,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
function getDesktopBridgeWindow() {
|
|
10
|
+
if (typeof window === "undefined") {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return window;
|
|
14
|
+
}
|
|
15
|
+
function getElectrobunRendererRpc() {
|
|
16
|
+
const w = getDesktopBridgeWindow();
|
|
17
|
+
return w?.__ELIZA_ELECTROBUN_RPC__ ?? null;
|
|
18
|
+
}
|
|
19
|
+
async function invokeDesktopBridgeRequest(options) {
|
|
20
|
+
const rpc = getElectrobunRendererRpc();
|
|
21
|
+
const request = rpc?.request?.[options.rpcMethod];
|
|
22
|
+
if (request) {
|
|
23
|
+
return (await request(options.params));
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function subscribeDesktopBridgeEvent(options) {
|
|
28
|
+
const rpc = getElectrobunRendererRpc();
|
|
29
|
+
if (rpc) {
|
|
30
|
+
rpc.onMessage(options.rpcMessage, options.listener);
|
|
31
|
+
return () => {
|
|
32
|
+
rpc.offMessage(options.rpcMessage, options.listener);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return () => { };
|
|
36
|
+
}
|
|
37
|
+
const getSpeechRecognition = () => window.SpeechRecognition ||
|
|
38
|
+
window.webkitSpeechRecognition ||
|
|
39
|
+
null;
|
|
40
|
+
/**
|
|
41
|
+
* WakeWordGate detects trigger phrases in transcripts.
|
|
42
|
+
*
|
|
43
|
+
* LIMITATION: Web Speech API does not provide word-level timing data.
|
|
44
|
+
* Unlike native implementations, we cannot measure post-trigger gaps.
|
|
45
|
+
* The `postGap` returned is always -1 (unavailable), and minPostTriggerGap is ignored.
|
|
46
|
+
* Detection is purely text-based: trigger phrase + subsequent command text.
|
|
47
|
+
*/
|
|
48
|
+
class WakeWordGate {
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.triggers = config.triggers.map((t) => t.toLowerCase().trim());
|
|
51
|
+
this.minCommandLength = config.minCommandLength ?? 1;
|
|
52
|
+
// Note: minPostTriggerGap cannot be enforced - Web Speech API lacks timing data
|
|
53
|
+
}
|
|
54
|
+
updateConfig(config) {
|
|
55
|
+
if (config.triggers)
|
|
56
|
+
this.triggers = config.triggers.map((t) => t.toLowerCase().trim());
|
|
57
|
+
if (config.minCommandLength !== undefined)
|
|
58
|
+
this.minCommandLength = config.minCommandLength;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Match wake word in transcript using text-only detection.
|
|
62
|
+
* Returns postGap=-1 to indicate timing data is unavailable on web.
|
|
63
|
+
*/
|
|
64
|
+
match(transcript) {
|
|
65
|
+
const normalizedTranscript = transcript.toLowerCase();
|
|
66
|
+
for (const trigger of this.triggers) {
|
|
67
|
+
const triggerIndex = normalizedTranscript.indexOf(trigger);
|
|
68
|
+
if (triggerIndex === -1)
|
|
69
|
+
continue;
|
|
70
|
+
// Extract command after the trigger phrase
|
|
71
|
+
const commandStart = triggerIndex + trigger.length;
|
|
72
|
+
const command = transcript.slice(commandStart).trim();
|
|
73
|
+
if (command.length < this.minCommandLength)
|
|
74
|
+
continue;
|
|
75
|
+
// postGap=-1 indicates timing unavailable on web platform
|
|
76
|
+
return { wakeWord: trigger, command, postGap: -1 };
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
class SwabbleWeb extends core.WebPlugin {
|
|
82
|
+
constructor() {
|
|
83
|
+
super(...arguments);
|
|
84
|
+
this.recognition = null;
|
|
85
|
+
this.config = null;
|
|
86
|
+
this.wakeGate = null;
|
|
87
|
+
this.isActive = false;
|
|
88
|
+
this.segments = [];
|
|
89
|
+
this.audioContext = null;
|
|
90
|
+
this.analyser = null;
|
|
91
|
+
this.mediaStream = null;
|
|
92
|
+
this.levelInterval = null;
|
|
93
|
+
// Native IPC state (Electrobun)
|
|
94
|
+
this.captureStream = null;
|
|
95
|
+
this.captureContext = null;
|
|
96
|
+
this.captureProcessor = null;
|
|
97
|
+
this.bridgeSubscriptions = [];
|
|
98
|
+
this.usingNativeIpc = false;
|
|
99
|
+
}
|
|
100
|
+
getRendererRpc() {
|
|
101
|
+
return getElectrobunRendererRpc() ?? null;
|
|
102
|
+
}
|
|
103
|
+
subscribeDesktopEvent(options) {
|
|
104
|
+
this.bridgeSubscriptions.push(subscribeDesktopBridgeEvent(options));
|
|
105
|
+
}
|
|
106
|
+
async invokeDesktopRequest(options) {
|
|
107
|
+
return await invokeDesktopBridgeRequest(options);
|
|
108
|
+
}
|
|
109
|
+
setupNativeListeners() {
|
|
110
|
+
this.removeNativeListeners();
|
|
111
|
+
this.subscribeDesktopEvent({
|
|
112
|
+
rpcMessage: "swabbleWakeWord",
|
|
113
|
+
ipcChannel: "swabble:wakeWord",
|
|
114
|
+
listener: (payload) => {
|
|
115
|
+
this.notifyListeners("wakeWord", payload);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
this.subscribeDesktopEvent({
|
|
119
|
+
rpcMessage: "swabbleStateChanged",
|
|
120
|
+
ipcChannel: "swabble:stateChange",
|
|
121
|
+
listener: (payload) => {
|
|
122
|
+
const listening = typeof payload.listening === "boolean"
|
|
123
|
+
? payload.listening
|
|
124
|
+
: false;
|
|
125
|
+
this.isActive = listening;
|
|
126
|
+
this.notifyListeners("stateChange", {
|
|
127
|
+
state: listening ? "listening" : "idle",
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
this.subscribeDesktopEvent({
|
|
132
|
+
rpcMessage: "swabbleTranscript",
|
|
133
|
+
ipcChannel: "swabble:transcript",
|
|
134
|
+
listener: (payload) => {
|
|
135
|
+
this.notifyListeners("transcript", payload);
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
this.subscribeDesktopEvent({
|
|
139
|
+
rpcMessage: "swabbleError",
|
|
140
|
+
ipcChannel: "swabble:error",
|
|
141
|
+
listener: (payload) => {
|
|
142
|
+
this.notifyListeners("error", payload);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
removeNativeListeners() {
|
|
147
|
+
for (const unsubscribe of this.bridgeSubscriptions) {
|
|
148
|
+
unsubscribe();
|
|
149
|
+
}
|
|
150
|
+
this.bridgeSubscriptions = [];
|
|
151
|
+
}
|
|
152
|
+
async startNativeAudioCapture(sampleRate = 16000) {
|
|
153
|
+
const rpcRequest = this.getRendererRpc()?.request?.swabbleAudioChunk;
|
|
154
|
+
const stream = await navigator.mediaDevices
|
|
155
|
+
.getUserMedia({ audio: true })
|
|
156
|
+
.catch(() => null);
|
|
157
|
+
if (!stream)
|
|
158
|
+
return;
|
|
159
|
+
this.captureStream = stream;
|
|
160
|
+
this.captureContext = new AudioContext();
|
|
161
|
+
const source = this.captureContext.createMediaStreamSource(stream);
|
|
162
|
+
const processor = this.captureContext.createScriptProcessor(4096, 1, 1);
|
|
163
|
+
this.captureProcessor = processor;
|
|
164
|
+
const inputRate = this.captureContext.sampleRate;
|
|
165
|
+
processor.onaudioprocess = (e) => {
|
|
166
|
+
const input = e.inputBuffer.getChannelData(0);
|
|
167
|
+
this.notifyListeners("audioLevel", {
|
|
168
|
+
level: this.computeRms(input),
|
|
169
|
+
peak: this.computePeak(input),
|
|
170
|
+
});
|
|
171
|
+
const ratio = inputRate / sampleRate;
|
|
172
|
+
const out = new Float32Array(Math.round(input.length / ratio));
|
|
173
|
+
for (let i = 0; i < out.length; i++) {
|
|
174
|
+
let acc = 0;
|
|
175
|
+
let cnt = 0;
|
|
176
|
+
const start = Math.round(i * ratio);
|
|
177
|
+
const end = Math.round((i + 1) * ratio);
|
|
178
|
+
for (let j = start; j < end && j < input.length; j++) {
|
|
179
|
+
acc += input[j];
|
|
180
|
+
cnt++;
|
|
181
|
+
}
|
|
182
|
+
out[i] = cnt > 0 ? acc / cnt : 0;
|
|
183
|
+
}
|
|
184
|
+
const bytes = new Uint8Array(out.buffer, out.byteOffset, out.byteLength);
|
|
185
|
+
let binary = "";
|
|
186
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
187
|
+
binary += String.fromCharCode(bytes[i]);
|
|
188
|
+
}
|
|
189
|
+
if (rpcRequest) {
|
|
190
|
+
void rpcRequest({ data: btoa(binary) }).catch(() => { });
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
source.connect(processor);
|
|
194
|
+
const sink = this.captureContext.createGain();
|
|
195
|
+
sink.gain.value = 0;
|
|
196
|
+
processor.connect(sink);
|
|
197
|
+
sink.connect(this.captureContext.destination);
|
|
198
|
+
}
|
|
199
|
+
computeRms(samples) {
|
|
200
|
+
let sum = 0;
|
|
201
|
+
for (let i = 0; i < samples.length; i++) {
|
|
202
|
+
sum += samples[i] * samples[i];
|
|
203
|
+
}
|
|
204
|
+
return Math.sqrt(sum / samples.length);
|
|
205
|
+
}
|
|
206
|
+
computePeak(samples) {
|
|
207
|
+
let peak = 0;
|
|
208
|
+
for (let i = 0; i < samples.length; i++) {
|
|
209
|
+
const value = Math.abs(samples[i]);
|
|
210
|
+
if (value > peak)
|
|
211
|
+
peak = value;
|
|
212
|
+
}
|
|
213
|
+
return peak;
|
|
214
|
+
}
|
|
215
|
+
stopNativeAudioCapture() {
|
|
216
|
+
this.captureProcessor?.disconnect();
|
|
217
|
+
this.captureProcessor = null;
|
|
218
|
+
this.captureContext?.close();
|
|
219
|
+
this.captureContext = null;
|
|
220
|
+
this.captureStream?.getTracks().forEach((t) => {
|
|
221
|
+
t.stop();
|
|
222
|
+
});
|
|
223
|
+
this.captureStream = null;
|
|
224
|
+
}
|
|
225
|
+
async start(options) {
|
|
226
|
+
if (this.isActive)
|
|
227
|
+
return { started: true };
|
|
228
|
+
// Delegate to the native desktop bridge when available.
|
|
229
|
+
const rpc = this.getRendererRpc();
|
|
230
|
+
if (rpc) {
|
|
231
|
+
try {
|
|
232
|
+
const result = await this.invokeDesktopRequest({
|
|
233
|
+
rpcMethod: "swabbleStart",
|
|
234
|
+
ipcChannel: "swabble:start",
|
|
235
|
+
params: options,
|
|
236
|
+
});
|
|
237
|
+
if (result?.started) {
|
|
238
|
+
this.isActive = true;
|
|
239
|
+
this.usingNativeIpc = true;
|
|
240
|
+
this.config = options.config;
|
|
241
|
+
this.setupNativeListeners();
|
|
242
|
+
await this.startNativeAudioCapture(options.config.sampleRate ?? 16000);
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Fall through to Web Speech API
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const SpeechRecognitionAPI = getSpeechRecognition();
|
|
251
|
+
if (!SpeechRecognitionAPI) {
|
|
252
|
+
return {
|
|
253
|
+
started: false,
|
|
254
|
+
error: "Speech recognition not supported in this browser",
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
this.config = options.config;
|
|
258
|
+
this.wakeGate = new WakeWordGate(options.config);
|
|
259
|
+
this.segments = [];
|
|
260
|
+
const recognition = new SpeechRecognitionAPI();
|
|
261
|
+
recognition.continuous = true;
|
|
262
|
+
recognition.interimResults = true;
|
|
263
|
+
recognition.lang = options.config.locale || "en-US";
|
|
264
|
+
recognition.onstart = () => {
|
|
265
|
+
this.isActive = true;
|
|
266
|
+
this.notifyListeners("stateChange", { state: "listening" });
|
|
267
|
+
};
|
|
268
|
+
recognition.onend = () => {
|
|
269
|
+
if (this.isActive) {
|
|
270
|
+
this.recognition?.start();
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.notifyListeners("stateChange", { state: "idle" });
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
recognition.onerror = (event) => {
|
|
277
|
+
const recoverable = event.error === "no-speech" || event.error === "aborted";
|
|
278
|
+
this.notifyListeners("error", {
|
|
279
|
+
code: event.error,
|
|
280
|
+
message: `Speech recognition error: ${event.error}`,
|
|
281
|
+
recoverable,
|
|
282
|
+
});
|
|
283
|
+
if (!recoverable) {
|
|
284
|
+
this.isActive = false;
|
|
285
|
+
this.notifyListeners("stateChange", {
|
|
286
|
+
state: "error",
|
|
287
|
+
reason: event.error,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
recognition.onresult = (event) => this.handleSpeechResult(event);
|
|
292
|
+
this.recognition = recognition;
|
|
293
|
+
await this.startAudioLevelMonitoring();
|
|
294
|
+
recognition.start();
|
|
295
|
+
return { started: true };
|
|
296
|
+
}
|
|
297
|
+
handleSpeechResult(event) {
|
|
298
|
+
let transcript = "";
|
|
299
|
+
let isFinal = false;
|
|
300
|
+
for (let i = 0; i < event.results.length; i++) {
|
|
301
|
+
transcript += event.results[i][0].transcript;
|
|
302
|
+
if (event.results[i].isFinal)
|
|
303
|
+
isFinal = true;
|
|
304
|
+
}
|
|
305
|
+
// Web Speech API does not provide word-level timing.
|
|
306
|
+
// Segments are provided for API compatibility but timing values are approximations.
|
|
307
|
+
const words = transcript.split(/\s+/).filter(Boolean);
|
|
308
|
+
this.segments = words.map((text) => ({
|
|
309
|
+
text,
|
|
310
|
+
start: -1, // Unavailable on web
|
|
311
|
+
duration: -1, // Unavailable on web
|
|
312
|
+
isFinal,
|
|
313
|
+
}));
|
|
314
|
+
const lastResult = event.results[event.results.length - 1];
|
|
315
|
+
const confidence = lastResult?.[0]?.confidence;
|
|
316
|
+
this.notifyListeners("transcript", {
|
|
317
|
+
transcript,
|
|
318
|
+
segments: this.segments,
|
|
319
|
+
isFinal,
|
|
320
|
+
confidence,
|
|
321
|
+
});
|
|
322
|
+
if (isFinal && this.wakeGate) {
|
|
323
|
+
const match = this.wakeGate.match(transcript);
|
|
324
|
+
if (match) {
|
|
325
|
+
this.notifyListeners("wakeWord", { ...match, transcript, confidence });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async startAudioLevelMonitoring() {
|
|
330
|
+
const stream = await navigator.mediaDevices
|
|
331
|
+
.getUserMedia({ audio: true })
|
|
332
|
+
.catch(() => null);
|
|
333
|
+
if (!stream)
|
|
334
|
+
return;
|
|
335
|
+
this.mediaStream = stream;
|
|
336
|
+
this.audioContext = new AudioContext();
|
|
337
|
+
this.analyser = this.audioContext.createAnalyser();
|
|
338
|
+
this.analyser.fftSize = 256;
|
|
339
|
+
this.audioContext.createMediaStreamSource(stream).connect(this.analyser);
|
|
340
|
+
const dataArray = new Uint8Array(this.analyser.frequencyBinCount);
|
|
341
|
+
this.levelInterval = setInterval(() => {
|
|
342
|
+
if (!this.analyser)
|
|
343
|
+
return;
|
|
344
|
+
this.analyser.getByteFrequencyData(dataArray);
|
|
345
|
+
const sum = dataArray.reduce((a, b) => a + b, 0);
|
|
346
|
+
this.notifyListeners("audioLevel", {
|
|
347
|
+
level: sum / dataArray.length / 255,
|
|
348
|
+
peak: Math.max(...dataArray) / 255,
|
|
349
|
+
});
|
|
350
|
+
}, 100);
|
|
351
|
+
}
|
|
352
|
+
stopAudioLevelMonitoring() {
|
|
353
|
+
if (this.levelInterval)
|
|
354
|
+
clearInterval(this.levelInterval);
|
|
355
|
+
this.levelInterval = null;
|
|
356
|
+
this.audioContext?.close();
|
|
357
|
+
this.audioContext = null;
|
|
358
|
+
this.mediaStream?.getTracks().forEach((t) => {
|
|
359
|
+
t.stop();
|
|
360
|
+
});
|
|
361
|
+
this.mediaStream = null;
|
|
362
|
+
this.analyser = null;
|
|
363
|
+
}
|
|
364
|
+
async stop() {
|
|
365
|
+
this.isActive = false;
|
|
366
|
+
// Clean up native IPC if in native mode
|
|
367
|
+
if (this.usingNativeIpc) {
|
|
368
|
+
this.usingNativeIpc = false;
|
|
369
|
+
this.removeNativeListeners();
|
|
370
|
+
this.stopNativeAudioCapture();
|
|
371
|
+
void this.invokeDesktopRequest({
|
|
372
|
+
rpcMethod: "swabbleStop",
|
|
373
|
+
ipcChannel: "swabble:stop",
|
|
374
|
+
});
|
|
375
|
+
this.notifyListeners("stateChange", { state: "idle" });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (this.recognition) {
|
|
379
|
+
this.recognition.stop();
|
|
380
|
+
this.recognition = null;
|
|
381
|
+
}
|
|
382
|
+
this.stopAudioLevelMonitoring();
|
|
383
|
+
this.notifyListeners("stateChange", { state: "idle" });
|
|
384
|
+
}
|
|
385
|
+
async isListening() {
|
|
386
|
+
return { listening: this.isActive };
|
|
387
|
+
}
|
|
388
|
+
async getConfig() {
|
|
389
|
+
return { config: this.config };
|
|
390
|
+
}
|
|
391
|
+
async updateConfig(options) {
|
|
392
|
+
if (this.config) {
|
|
393
|
+
this.config = { ...this.config, ...options.config };
|
|
394
|
+
this.wakeGate?.updateConfig(options.config);
|
|
395
|
+
if (options.config.locale && this.recognition) {
|
|
396
|
+
this.recognition.lang = options.config.locale;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Sync to native IPC if active
|
|
400
|
+
if (this.usingNativeIpc) {
|
|
401
|
+
void this.invokeDesktopRequest({
|
|
402
|
+
rpcMethod: "swabbleUpdateConfig",
|
|
403
|
+
ipcChannel: "swabble:updateConfig",
|
|
404
|
+
params: options.config,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async checkPermissions() {
|
|
409
|
+
let microphone = "prompt";
|
|
410
|
+
try {
|
|
411
|
+
const result = await navigator.permissions.query({
|
|
412
|
+
name: "microphone",
|
|
413
|
+
});
|
|
414
|
+
microphone = result.state;
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
/* permissions.query not supported for microphone in some browsers */
|
|
418
|
+
}
|
|
419
|
+
let speechRecognition = getSpeechRecognition() ? "granted" : "not_supported";
|
|
420
|
+
const whisperStatus = await this.invokeDesktopRequest({
|
|
421
|
+
rpcMethod: "swabbleIsWhisperAvailable",
|
|
422
|
+
ipcChannel: "swabble:isWhisperAvailable",
|
|
423
|
+
});
|
|
424
|
+
if (whisperStatus?.available) {
|
|
425
|
+
speechRecognition = "granted";
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
microphone,
|
|
429
|
+
speechRecognition,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
async requestPermissions() {
|
|
433
|
+
try {
|
|
434
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
435
|
+
stream.getTracks().forEach((track) => {
|
|
436
|
+
track.stop();
|
|
437
|
+
});
|
|
438
|
+
return this.checkPermissions();
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
return {
|
|
442
|
+
microphone: "denied",
|
|
443
|
+
speechRecognition: "denied",
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async getAudioDevices() {
|
|
448
|
+
try {
|
|
449
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
450
|
+
const audioInputs = devices
|
|
451
|
+
.filter((d) => d.kind === "audioinput")
|
|
452
|
+
.map((d, i) => ({
|
|
453
|
+
id: d.deviceId,
|
|
454
|
+
name: d.label || `Microphone ${i + 1}`,
|
|
455
|
+
isDefault: d.deviceId === "default",
|
|
456
|
+
}));
|
|
457
|
+
return { devices: audioInputs };
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
return { devices: [] };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async setAudioDevice(_options) {
|
|
464
|
+
// Web Speech API doesn't support device selection directly.
|
|
465
|
+
// The browser uses its default audio input device.
|
|
466
|
+
throw new Error("setAudioDevice is not supported on web platform - browser uses system default audio input");
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
471
|
+
__proto__: null,
|
|
472
|
+
SwabbleWeb: SwabbleWeb
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
exports.Swabble = Swabble;
|
|
476
|
+
|
|
477
|
+
return exports;
|
|
478
|
+
|
|
479
|
+
})({}, capacitorExports);
|
|
480
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((m) => new m.SwabbleWeb());\nexport const Swabble = registerPlugin(\"Swabble\", {\n web: loadWeb,\n});\n","import { WebPlugin } from \"@capacitor/core\";\nfunction getDesktopBridgeWindow() {\n if (typeof window === \"undefined\") {\n return null;\n }\n return window;\n}\nfunction getElectrobunRendererRpc() {\n const w = getDesktopBridgeWindow();\n return w?.__ELIZA_ELECTROBUN_RPC__ ?? null;\n}\nasync function invokeDesktopBridgeRequest(options) {\n const rpc = getElectrobunRendererRpc();\n const request = rpc?.request?.[options.rpcMethod];\n if (request) {\n return (await request(options.params));\n }\n return null;\n}\nfunction subscribeDesktopBridgeEvent(options) {\n const rpc = getElectrobunRendererRpc();\n if (rpc) {\n rpc.onMessage(options.rpcMessage, options.listener);\n return () => {\n rpc.offMessage(options.rpcMessage, options.listener);\n };\n }\n return () => { };\n}\nconst getSpeechRecognition = () => window.SpeechRecognition ||\n window.webkitSpeechRecognition ||\n null;\n/**\n * WakeWordGate detects trigger phrases in transcripts.\n *\n * LIMITATION: Web Speech API does not provide word-level timing data.\n * Unlike native implementations, we cannot measure post-trigger gaps.\n * The `postGap` returned is always -1 (unavailable), and minPostTriggerGap is ignored.\n * Detection is purely text-based: trigger phrase + subsequent command text.\n */\nclass WakeWordGate {\n constructor(config) {\n this.triggers = config.triggers.map((t) => t.toLowerCase().trim());\n this.minCommandLength = config.minCommandLength ?? 1;\n // Note: minPostTriggerGap cannot be enforced - Web Speech API lacks timing data\n }\n updateConfig(config) {\n if (config.triggers)\n this.triggers = config.triggers.map((t) => t.toLowerCase().trim());\n if (config.minCommandLength !== undefined)\n this.minCommandLength = config.minCommandLength;\n }\n /**\n * Match wake word in transcript using text-only detection.\n * Returns postGap=-1 to indicate timing data is unavailable on web.\n */\n match(transcript) {\n const normalizedTranscript = transcript.toLowerCase();\n for (const trigger of this.triggers) {\n const triggerIndex = normalizedTranscript.indexOf(trigger);\n if (triggerIndex === -1)\n continue;\n // Extract command after the trigger phrase\n const commandStart = triggerIndex + trigger.length;\n const command = transcript.slice(commandStart).trim();\n if (command.length < this.minCommandLength)\n continue;\n // postGap=-1 indicates timing unavailable on web platform\n return { wakeWord: trigger, command, postGap: -1 };\n }\n return null;\n }\n}\nexport class SwabbleWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.recognition = null;\n this.config = null;\n this.wakeGate = null;\n this.isActive = false;\n this.segments = [];\n this.audioContext = null;\n this.analyser = null;\n this.mediaStream = null;\n this.levelInterval = null;\n // Native IPC state (Electrobun)\n this.captureStream = null;\n this.captureContext = null;\n this.captureProcessor = null;\n this.bridgeSubscriptions = [];\n this.usingNativeIpc = false;\n }\n getRendererRpc() {\n return getElectrobunRendererRpc() ?? null;\n }\n subscribeDesktopEvent(options) {\n this.bridgeSubscriptions.push(subscribeDesktopBridgeEvent(options));\n }\n async invokeDesktopRequest(options) {\n return await invokeDesktopBridgeRequest(options);\n }\n setupNativeListeners() {\n this.removeNativeListeners();\n this.subscribeDesktopEvent({\n rpcMessage: \"swabbleWakeWord\",\n ipcChannel: \"swabble:wakeWord\",\n listener: (payload) => {\n this.notifyListeners(\"wakeWord\", payload);\n },\n });\n this.subscribeDesktopEvent({\n rpcMessage: \"swabbleStateChanged\",\n ipcChannel: \"swabble:stateChange\",\n listener: (payload) => {\n const listening = typeof payload.listening === \"boolean\"\n ? payload.listening\n : false;\n this.isActive = listening;\n this.notifyListeners(\"stateChange\", {\n state: listening ? \"listening\" : \"idle\",\n });\n },\n });\n this.subscribeDesktopEvent({\n rpcMessage: \"swabbleTranscript\",\n ipcChannel: \"swabble:transcript\",\n listener: (payload) => {\n this.notifyListeners(\"transcript\", payload);\n },\n });\n this.subscribeDesktopEvent({\n rpcMessage: \"swabbleError\",\n ipcChannel: \"swabble:error\",\n listener: (payload) => {\n this.notifyListeners(\"error\", payload);\n },\n });\n }\n removeNativeListeners() {\n for (const unsubscribe of this.bridgeSubscriptions) {\n unsubscribe();\n }\n this.bridgeSubscriptions = [];\n }\n async startNativeAudioCapture(sampleRate = 16000) {\n const rpcRequest = this.getRendererRpc()?.request?.swabbleAudioChunk;\n const stream = await navigator.mediaDevices\n .getUserMedia({ audio: true })\n .catch(() => null);\n if (!stream)\n return;\n this.captureStream = stream;\n this.captureContext = new AudioContext();\n const source = this.captureContext.createMediaStreamSource(stream);\n const processor = this.captureContext.createScriptProcessor(4096, 1, 1);\n this.captureProcessor = processor;\n const inputRate = this.captureContext.sampleRate;\n processor.onaudioprocess = (e) => {\n const input = e.inputBuffer.getChannelData(0);\n this.notifyListeners(\"audioLevel\", {\n level: this.computeRms(input),\n peak: this.computePeak(input),\n });\n const ratio = inputRate / sampleRate;\n const out = new Float32Array(Math.round(input.length / ratio));\n for (let i = 0; i < out.length; i++) {\n let acc = 0;\n let cnt = 0;\n const start = Math.round(i * ratio);\n const end = Math.round((i + 1) * ratio);\n for (let j = start; j < end && j < input.length; j++) {\n acc += input[j];\n cnt++;\n }\n out[i] = cnt > 0 ? acc / cnt : 0;\n }\n const bytes = new Uint8Array(out.buffer, out.byteOffset, out.byteLength);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n if (rpcRequest) {\n void rpcRequest({ data: btoa(binary) }).catch(() => { });\n }\n };\n source.connect(processor);\n const sink = this.captureContext.createGain();\n sink.gain.value = 0;\n processor.connect(sink);\n sink.connect(this.captureContext.destination);\n }\n computeRms(samples) {\n let sum = 0;\n for (let i = 0; i < samples.length; i++) {\n sum += samples[i] * samples[i];\n }\n return Math.sqrt(sum / samples.length);\n }\n computePeak(samples) {\n let peak = 0;\n for (let i = 0; i < samples.length; i++) {\n const value = Math.abs(samples[i]);\n if (value > peak)\n peak = value;\n }\n return peak;\n }\n stopNativeAudioCapture() {\n this.captureProcessor?.disconnect();\n this.captureProcessor = null;\n this.captureContext?.close();\n this.captureContext = null;\n this.captureStream?.getTracks().forEach((t) => {\n t.stop();\n });\n this.captureStream = null;\n }\n async start(options) {\n if (this.isActive)\n return { started: true };\n // Delegate to the native desktop bridge when available.\n const rpc = this.getRendererRpc();\n if (rpc) {\n try {\n const result = await this.invokeDesktopRequest({\n rpcMethod: \"swabbleStart\",\n ipcChannel: \"swabble:start\",\n params: options,\n });\n if (result?.started) {\n this.isActive = true;\n this.usingNativeIpc = true;\n this.config = options.config;\n this.setupNativeListeners();\n await this.startNativeAudioCapture(options.config.sampleRate ?? 16000);\n return result;\n }\n }\n catch {\n // Fall through to Web Speech API\n }\n }\n const SpeechRecognitionAPI = getSpeechRecognition();\n if (!SpeechRecognitionAPI) {\n return {\n started: false,\n error: \"Speech recognition not supported in this browser\",\n };\n }\n this.config = options.config;\n this.wakeGate = new WakeWordGate(options.config);\n this.segments = [];\n const recognition = new SpeechRecognitionAPI();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = options.config.locale || \"en-US\";\n recognition.onstart = () => {\n this.isActive = true;\n this.notifyListeners(\"stateChange\", { state: \"listening\" });\n };\n recognition.onend = () => {\n if (this.isActive) {\n this.recognition?.start();\n }\n else {\n this.notifyListeners(\"stateChange\", { state: \"idle\" });\n }\n };\n recognition.onerror = (event) => {\n const recoverable = event.error === \"no-speech\" || event.error === \"aborted\";\n this.notifyListeners(\"error\", {\n code: event.error,\n message: `Speech recognition error: ${event.error}`,\n recoverable,\n });\n if (!recoverable) {\n this.isActive = false;\n this.notifyListeners(\"stateChange\", {\n state: \"error\",\n reason: event.error,\n });\n }\n };\n recognition.onresult = (event) => this.handleSpeechResult(event);\n this.recognition = recognition;\n await this.startAudioLevelMonitoring();\n recognition.start();\n return { started: true };\n }\n handleSpeechResult(event) {\n let transcript = \"\";\n let isFinal = false;\n for (let i = 0; i < event.results.length; i++) {\n transcript += event.results[i][0].transcript;\n if (event.results[i].isFinal)\n isFinal = true;\n }\n // Web Speech API does not provide word-level timing.\n // Segments are provided for API compatibility but timing values are approximations.\n const words = transcript.split(/\\s+/).filter(Boolean);\n this.segments = words.map((text) => ({\n text,\n start: -1, // Unavailable on web\n duration: -1, // Unavailable on web\n isFinal,\n }));\n const lastResult = event.results[event.results.length - 1];\n const confidence = lastResult?.[0]?.confidence;\n this.notifyListeners(\"transcript\", {\n transcript,\n segments: this.segments,\n isFinal,\n confidence,\n });\n if (isFinal && this.wakeGate) {\n const match = this.wakeGate.match(transcript);\n if (match) {\n this.notifyListeners(\"wakeWord\", { ...match, transcript, confidence });\n }\n }\n }\n async startAudioLevelMonitoring() {\n const stream = await navigator.mediaDevices\n .getUserMedia({ audio: true })\n .catch(() => null);\n if (!stream)\n return;\n this.mediaStream = stream;\n this.audioContext = new AudioContext();\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = 256;\n this.audioContext.createMediaStreamSource(stream).connect(this.analyser);\n const dataArray = new Uint8Array(this.analyser.frequencyBinCount);\n this.levelInterval = setInterval(() => {\n if (!this.analyser)\n return;\n this.analyser.getByteFrequencyData(dataArray);\n const sum = dataArray.reduce((a, b) => a + b, 0);\n this.notifyListeners(\"audioLevel\", {\n level: sum / dataArray.length / 255,\n peak: Math.max(...dataArray) / 255,\n });\n }, 100);\n }\n stopAudioLevelMonitoring() {\n if (this.levelInterval)\n clearInterval(this.levelInterval);\n this.levelInterval = null;\n this.audioContext?.close();\n this.audioContext = null;\n this.mediaStream?.getTracks().forEach((t) => {\n t.stop();\n });\n this.mediaStream = null;\n this.analyser = null;\n }\n async stop() {\n this.isActive = false;\n // Clean up native IPC if in native mode\n if (this.usingNativeIpc) {\n this.usingNativeIpc = false;\n this.removeNativeListeners();\n this.stopNativeAudioCapture();\n void this.invokeDesktopRequest({\n rpcMethod: \"swabbleStop\",\n ipcChannel: \"swabble:stop\",\n });\n this.notifyListeners(\"stateChange\", { state: \"idle\" });\n return;\n }\n if (this.recognition) {\n this.recognition.stop();\n this.recognition = null;\n }\n this.stopAudioLevelMonitoring();\n this.notifyListeners(\"stateChange\", { state: \"idle\" });\n }\n async isListening() {\n return { listening: this.isActive };\n }\n async getConfig() {\n return { config: this.config };\n }\n async updateConfig(options) {\n if (this.config) {\n this.config = { ...this.config, ...options.config };\n this.wakeGate?.updateConfig(options.config);\n if (options.config.locale && this.recognition) {\n this.recognition.lang = options.config.locale;\n }\n }\n // Sync to native IPC if active\n if (this.usingNativeIpc) {\n void this.invokeDesktopRequest({\n rpcMethod: \"swabbleUpdateConfig\",\n ipcChannel: \"swabble:updateConfig\",\n params: options.config,\n });\n }\n }\n async checkPermissions() {\n let microphone = \"prompt\";\n try {\n const result = await navigator.permissions.query({\n name: \"microphone\",\n });\n microphone = result.state;\n }\n catch {\n /* permissions.query not supported for microphone in some browsers */\n }\n let speechRecognition = getSpeechRecognition() ? \"granted\" : \"not_supported\";\n const whisperStatus = await this.invokeDesktopRequest({\n rpcMethod: \"swabbleIsWhisperAvailable\",\n ipcChannel: \"swabble:isWhisperAvailable\",\n });\n if (whisperStatus?.available) {\n speechRecognition = \"granted\";\n }\n return {\n microphone,\n speechRecognition,\n };\n }\n async requestPermissions() {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach((track) => {\n track.stop();\n });\n return this.checkPermissions();\n }\n catch {\n return {\n microphone: \"denied\",\n speechRecognition: \"denied\",\n };\n }\n }\n async getAudioDevices() {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const audioInputs = devices\n .filter((d) => d.kind === \"audioinput\")\n .map((d, i) => ({\n id: d.deviceId,\n name: d.label || `Microphone ${i + 1}`,\n isDefault: d.deviceId === \"default\",\n }));\n return { devices: audioInputs };\n }\n catch {\n return { devices: [] };\n }\n }\n async setAudioDevice(_options) {\n // Web Speech API doesn't support device selection directly.\n // The browser uses its default audio input device.\n throw new Error(\"setAudioDevice is not supported on web platform - browser uses system default audio input\");\n }\n}\n"],"names":["registerPlugin","WebPlugin"],"mappings":";;;IAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;AACzD,UAAC,OAAO,GAAGA,mBAAc,CAAC,SAAS,EAAE;IACjD,IAAI,GAAG,EAAE,OAAO;IAChB,CAAC;;ICJD,SAAS,sBAAsB,GAAG;IAClC,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;IACvC,QAAQ,OAAO,IAAI;IACnB,IAAI;IACJ,IAAI,OAAO,MAAM;IACjB;IACA,SAAS,wBAAwB,GAAG;IACpC,IAAI,MAAM,CAAC,GAAG,sBAAsB,EAAE;IACtC,IAAI,OAAO,CAAC,EAAE,wBAAwB,IAAI,IAAI;IAC9C;IACA,eAAe,0BAA0B,CAAC,OAAO,EAAE;IACnD,IAAI,MAAM,GAAG,GAAG,wBAAwB,EAAE;IAC1C,IAAI,MAAM,OAAO,GAAG,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IACrD,IAAI,IAAI,OAAO,EAAE;IACjB,QAAQ,QAAQ,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7C,IAAI;IACJ,IAAI,OAAO,IAAI;IACf;IACA,SAAS,2BAA2B,CAAC,OAAO,EAAE;IAC9C,IAAI,MAAM,GAAG,GAAG,wBAAwB,EAAE;IAC1C,IAAI,IAAI,GAAG,EAAE;IACb,QAAQ,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC;IAC3D,QAAQ,OAAO,MAAM;IACrB,YAAY,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC;IAChE,QAAQ,CAAC;IACT,IAAI;IACJ,IAAI,OAAO,MAAM,EAAE,CAAC;IACpB;IACA,MAAM,oBAAoB,GAAG,MAAM,MAAM,CAAC,iBAAiB;IAC3D,IAAI,MAAM,CAAC,uBAAuB;IAClC,IAAI,IAAI;IACR;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,MAAM,YAAY,CAAC;IACnB,IAAI,WAAW,CAAC,MAAM,EAAE;IACxB,QAAQ,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC1E,QAAQ,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,CAAC;IAC5D;IACA,IAAI;IACJ,IAAI,YAAY,CAAC,MAAM,EAAE;IACzB,QAAQ,IAAI,MAAM,CAAC,QAAQ;IAC3B,YAAY,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9E,QAAQ,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS;IACjD,YAAY,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB;IAC3D,IAAI;IACJ;IACA;IACA;IACA;IACA,IAAI,KAAK,CAAC,UAAU,EAAE;IACtB,QAAQ,MAAM,oBAAoB,GAAG,UAAU,CAAC,WAAW,EAAE;IAC7D,QAAQ,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE;IAC7C,YAAY,MAAM,YAAY,GAAG,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC;IACtE,YAAY,IAAI,YAAY,KAAK,EAAE;IACnC,gBAAgB;IAChB;IACA,YAAY,MAAM,YAAY,GAAG,YAAY,GAAG,OAAO,CAAC,MAAM;IAC9D,YAAY,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE;IACjE,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,gBAAgB;IACtD,gBAAgB;IAChB;IACA,YAAY,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;IAC9D,QAAQ;IACR,QAAQ,OAAO,IAAI;IACnB,IAAI;IACJ;IACO,MAAM,UAAU,SAASC,cAAS,CAAC;IAC1C,IAAI,WAAW,GAAG;IAClB,QAAQ,KAAK,CAAC,GAAG,SAAS,CAAC;IAC3B,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI;IAC/B,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI;IAC1B,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI;IAC5B,QAAQ,IAAI,CAAC,QAAQ,GAAG,KAAK;IAC7B,QAAQ,IAAI,CAAC,QAAQ,GAAG,EAAE;IAC1B,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI;IAChC,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI;IAC5B,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI;IAC/B,QAAQ,IAAI,CAAC,aAAa,GAAG,IAAI;IACjC;IACA,QAAQ,IAAI,CAAC,aAAa,GAAG,IAAI;IACjC,QAAQ,IAAI,CAAC,cAAc,GAAG,IAAI;IAClC,QAAQ,IAAI,CAAC,gBAAgB,GAAG,IAAI;IACpC,QAAQ,IAAI,CAAC,mBAAmB,GAAG,EAAE;IACrC,QAAQ,IAAI,CAAC,cAAc,GAAG,KAAK;IACnC,IAAI;IACJ,IAAI,cAAc,GAAG;IACrB,QAAQ,OAAO,wBAAwB,EAAE,IAAI,IAAI;IACjD,IAAI;IACJ,IAAI,qBAAqB,CAAC,OAAO,EAAE;IACnC,QAAQ,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC;IAC3E,IAAI;IACJ,IAAI,MAAM,oBAAoB,CAAC,OAAO,EAAE;IACxC,QAAQ,OAAO,MAAM,0BAA0B,CAAC,OAAO,CAAC;IACxD,IAAI;IACJ,IAAI,oBAAoB,GAAG;IAC3B,QAAQ,IAAI,CAAC,qBAAqB,EAAE;IACpC,QAAQ,IAAI,CAAC,qBAAqB,CAAC;IACnC,YAAY,UAAU,EAAE,iBAAiB;IACzC,YAAY,UAAU,EAAE,kBAAkB;IAC1C,YAAY,QAAQ,EAAE,CAAC,OAAO,KAAK;IACnC,gBAAgB,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC;IACzD,YAAY,CAAC;IACb,SAAS,CAAC;IACV,QAAQ,IAAI,CAAC,qBAAqB,CAAC;IACnC,YAAY,UAAU,EAAE,qBAAqB;IAC7C,YAAY,UAAU,EAAE,qBAAqB;IAC7C,YAAY,QAAQ,EAAE,CAAC,OAAO,KAAK;IACnC,gBAAgB,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,SAAS,KAAK;IAC/D,sBAAsB,OAAO,CAAC;IAC9B,sBAAsB,KAAK;IAC3B,gBAAgB,IAAI,CAAC,QAAQ,GAAG,SAAS;IACzC,gBAAgB,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE;IACpD,oBAAoB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM;IAC3D,iBAAiB,CAAC;IAClB,YAAY,CAAC;IACb,SAAS,CAAC;IACV,QAAQ,IAAI,CAAC,qBAAqB,CAAC;IACnC,YAAY,UAAU,EAAE,mBAAmB;IAC3C,YAAY,UAAU,EAAE,oBAAoB;IAC5C,YAAY,QAAQ,EAAE,CAAC,OAAO,KAAK;IACnC,gBAAgB,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,OAAO,CAAC;IAC3D,YAAY,CAAC;IACb,SAAS,CAAC;IACV,QAAQ,IAAI,CAAC,qBAAqB,CAAC;IACnC,YAAY,UAAU,EAAE,cAAc;IACtC,YAAY,UAAU,EAAE,eAAe;IACvC,YAAY,QAAQ,EAAE,CAAC,OAAO,KAAK;IACnC,gBAAgB,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC;IACtD,YAAY,CAAC;IACb,SAAS,CAAC;IACV,IAAI;IACJ,IAAI,qBAAqB,GAAG;IAC5B,QAAQ,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,mBAAmB,EAAE;IAC5D,YAAY,WAAW,EAAE;IACzB,QAAQ;IACR,QAAQ,IAAI,CAAC,mBAAmB,GAAG,EAAE;IACrC,IAAI;IACJ,IAAI,MAAM,uBAAuB,CAAC,UAAU,GAAG,KAAK,EAAE;IACtD,QAAQ,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,iBAAiB;IAC5E,QAAQ,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;IACvC,aAAa,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;IACzC,aAAa,KAAK,CAAC,MAAM,IAAI,CAAC;IAC9B,QAAQ,IAAI,CAAC,MAAM;IACnB,YAAY;IACZ,QAAQ,IAAI,CAAC,aAAa,GAAG,MAAM;IACnC,QAAQ,IAAI,CAAC,cAAc,GAAG,IAAI,YAAY,EAAE;IAChD,QAAQ,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,uBAAuB,CAAC,MAAM,CAAC;IAC1E,QAAQ,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/E,QAAQ,IAAI,CAAC,gBAAgB,GAAG,SAAS;IACzC,QAAQ,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU;IACxD,QAAQ,SAAS,CAAC,cAAc,GAAG,CAAC,CAAC,KAAK;IAC1C,YAAY,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;IACzD,YAAY,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE;IAC/C,gBAAgB,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;IAC7C,gBAAgB,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;IAC7C,aAAa,CAAC;IACd,YAAY,MAAM,KAAK,GAAG,SAAS,GAAG,UAAU;IAChD,YAAY,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;IAC1E,YAAY,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACjD,gBAAgB,IAAI,GAAG,GAAG,CAAC;IAC3B,gBAAgB,IAAI,GAAG,GAAG,CAAC;IAC3B,gBAAgB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC;IACnD,gBAAgB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC;IACvD,gBAAgB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACtE,oBAAoB,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC;IACnC,oBAAoB,GAAG,EAAE;IACzB,gBAAgB;IAChB,gBAAgB,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAChD,YAAY;IACZ,YAAY,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC;IACpF,YAAY,IAAI,MAAM,GAAG,EAAE;IAC3B,YAAY,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACnD,gBAAgB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvD,YAAY;IACZ,YAAY,IAAI,UAAU,EAAE;IAC5B,gBAAgB,KAAK,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,YAAY;IACZ,QAAQ,CAAC;IACT,QAAQ,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;IACjC,QAAQ,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE;IACrD,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC;IAC3B,QAAQ,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/B,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC;IACrD,IAAI;IACJ,IAAI,UAAU,CAAC,OAAO,EAAE;IACxB,QAAQ,IAAI,GAAG,GAAG,CAAC;IACnB,QAAQ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACjD,YAAY,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAC1C,QAAQ;IACR,QAAQ,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9C,IAAI;IACJ,IAAI,WAAW,CAAC,OAAO,EAAE;IACzB,QAAQ,IAAI,IAAI,GAAG,CAAC;IACpB,QAAQ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACjD,YAAY,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9C,YAAY,IAAI,KAAK,GAAG,IAAI;IAC5B,gBAAgB,IAAI,GAAG,KAAK;IAC5B,QAAQ;IACR,QAAQ,OAAO,IAAI;IACnB,IAAI;IACJ,IAAI,sBAAsB,GAAG;IAC7B,QAAQ,IAAI,CAAC,gBAAgB,EAAE,UAAU,EAAE;IAC3C,QAAQ,IAAI,CAAC,gBAAgB,GAAG,IAAI;IACpC,QAAQ,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE;IACpC,QAAQ,IAAI,CAAC,cAAc,GAAG,IAAI;IAClC,QAAQ,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;IACvD,YAAY,CAAC,CAAC,IAAI,EAAE;IACpB,QAAQ,CAAC,CAAC;IACV,QAAQ,IAAI,CAAC,aAAa,GAAG,IAAI;IACjC,IAAI;IACJ,IAAI,MAAM,KAAK,CAAC,OAAO,EAAE;IACzB,QAAQ,IAAI,IAAI,CAAC,QAAQ;IACzB,YAAY,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;IACpC;IACA,QAAQ,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,EAAE;IACzC,QAAQ,IAAI,GAAG,EAAE;IACjB,YAAY,IAAI;IAChB,gBAAgB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC;IAC/D,oBAAoB,SAAS,EAAE,cAAc;IAC7C,oBAAoB,UAAU,EAAE,eAAe;IAC/C,oBAAoB,MAAM,EAAE,OAAO;IACnC,iBAAiB,CAAC;IAClB,gBAAgB,IAAI,MAAM,EAAE,OAAO,EAAE;IACrC,oBAAoB,IAAI,CAAC,QAAQ,GAAG,IAAI;IACxC,oBAAoB,IAAI,CAAC,cAAc,GAAG,IAAI;IAC9C,oBAAoB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM;IAChD,oBAAoB,IAAI,CAAC,oBAAoB,EAAE;IAC/C,oBAAoB,MAAM,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC;IAC1F,oBAAoB,OAAO,MAAM;IACjC,gBAAgB;IAChB,YAAY;IACZ,YAAY,MAAM;IAClB;IACA,YAAY;IACZ,QAAQ;IACR,QAAQ,MAAM,oBAAoB,GAAG,oBAAoB,EAAE;IAC3D,QAAQ,IAAI,CAAC,oBAAoB,EAAE;IACnC,YAAY,OAAO;IACnB,gBAAgB,OAAO,EAAE,KAAK;IAC9B,gBAAgB,KAAK,EAAE,kDAAkD;IACzE,aAAa;IACb,QAAQ;IACR,QAAQ,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM;IACpC,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;IACxD,QAAQ,IAAI,CAAC,QAAQ,GAAG,EAAE;IAC1B,QAAQ,MAAM,WAAW,GAAG,IAAI,oBAAoB,EAAE;IACtD,QAAQ,WAAW,CAAC,UAAU,GAAG,IAAI;IACrC,QAAQ,WAAW,CAAC,cAAc,GAAG,IAAI;IACzC,QAAQ,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,OAAO;IAC3D,QAAQ,WAAW,CAAC,OAAO,GAAG,MAAM;IACpC,YAAY,IAAI,CAAC,QAAQ,GAAG,IAAI;IAChC,YAAY,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IACvE,QAAQ,CAAC;IACT,QAAQ,WAAW,CAAC,KAAK,GAAG,MAAM;IAClC,YAAY,IAAI,IAAI,CAAC,QAAQ,EAAE;IAC/B,gBAAgB,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE;IACzC,YAAY;IACZ,iBAAiB;IACjB,gBAAgB,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACtE,YAAY;IACZ,QAAQ,CAAC;IACT,QAAQ,WAAW,CAAC,OAAO,GAAG,CAAC,KAAK,KAAK;IACzC,YAAY,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;IACxF,YAAY,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE;IAC1C,gBAAgB,IAAI,EAAE,KAAK,CAAC,KAAK;IACjC,gBAAgB,OAAO,EAAE,CAAC,0BAA0B,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IACnE,gBAAgB,WAAW;IAC3B,aAAa,CAAC;IACd,YAAY,IAAI,CAAC,WAAW,EAAE;IAC9B,gBAAgB,IAAI,CAAC,QAAQ,GAAG,KAAK;IACrC,gBAAgB,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE;IACpD,oBAAoB,KAAK,EAAE,OAAO;IAClC,oBAAoB,MAAM,EAAE,KAAK,CAAC,KAAK;IACvC,iBAAiB,CAAC;IAClB,YAAY;IACZ,QAAQ,CAAC;IACT,QAAQ,WAAW,CAAC,QAAQ,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;IACxE,QAAQ,IAAI,CAAC,WAAW,GAAG,WAAW;IACtC,QAAQ,MAAM,IAAI,CAAC,yBAAyB,EAAE;IAC9C,QAAQ,WAAW,CAAC,KAAK,EAAE;IAC3B,QAAQ,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;IAChC,IAAI;IACJ,IAAI,kBAAkB,CAAC,KAAK,EAAE;IAC9B,QAAQ,IAAI,UAAU,GAAG,EAAE;IAC3B,QAAQ,IAAI,OAAO,GAAG,KAAK;IAC3B,QAAQ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;IACvD,YAAY,UAAU,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;IACxD,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;IACxC,gBAAgB,OAAO,GAAG,IAAI;IAC9B,QAAQ;IACR;IACA;IACA,QAAQ,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;IAC7D,QAAQ,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM;IAC7C,YAAY,IAAI;IAChB,YAAY,KAAK,EAAE,EAAE;IACrB,YAAY,QAAQ,EAAE,EAAE;IACxB,YAAY,OAAO;IACnB,SAAS,CAAC,CAAC;IACX,QAAQ,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAClE,QAAQ,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC,EAAE,UAAU;IACtD,QAAQ,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE;IAC3C,YAAY,UAAU;IACtB,YAAY,QAAQ,EAAE,IAAI,CAAC,QAAQ;IACnC,YAAY,OAAO;IACnB,YAAY,UAAU;IACtB,SAAS,CAAC;IACV,QAAQ,IAAI,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE;IACtC,YAAY,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC;IACzD,YAAY,IAAI,KAAK,EAAE;IACvB,gBAAgB,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;IACtF,YAAY;IACZ,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,yBAAyB,GAAG;IACtC,QAAQ,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;IACvC,aAAa,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;IACzC,aAAa,KAAK,CAAC,MAAM,IAAI,CAAC;IAC9B,QAAQ,IAAI,CAAC,MAAM;IACnB,YAAY;IACZ,QAAQ,IAAI,CAAC,WAAW,GAAG,MAAM;IACjC,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,EAAE;IAC9C,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;IAC1D,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,GAAG,GAAG;IACnC,QAAQ,IAAI,CAAC,YAAY,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;IAChF,QAAQ,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IACzE,QAAQ,IAAI,CAAC,aAAa,GAAG,WAAW,CAAC,MAAM;IAC/C,YAAY,IAAI,CAAC,IAAI,CAAC,QAAQ;IAC9B,gBAAgB;IAChB,YAAY,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,SAAS,CAAC;IACzD,YAAY,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5D,YAAY,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE;IAC/C,gBAAgB,KAAK,EAAE,GAAG,GAAG,SAAS,CAAC,MAAM,GAAG,GAAG;IACnD,gBAAgB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,GAAG;IAClD,aAAa,CAAC;IACd,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,IAAI;IACJ,IAAI,wBAAwB,GAAG;IAC/B,QAAQ,IAAI,IAAI,CAAC,aAAa;IAC9B,YAAY,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC;IAC7C,QAAQ,IAAI,CAAC,aAAa,GAAG,IAAI;IACjC,QAAQ,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE;IAClC,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI;IAChC,QAAQ,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;IACrD,YAAY,CAAC,CAAC,IAAI,EAAE;IACpB,QAAQ,CAAC,CAAC;IACV,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI;IAC/B,QAAQ,IAAI,CAAC,QAAQ,GAAG,IAAI;IAC5B,IAAI;IACJ,IAAI,MAAM,IAAI,GAAG;IACjB,QAAQ,IAAI,CAAC,QAAQ,GAAG,KAAK;IAC7B;IACA,QAAQ,IAAI,IAAI,CAAC,cAAc,EAAE;IACjC,YAAY,IAAI,CAAC,cAAc,GAAG,KAAK;IACvC,YAAY,IAAI,CAAC,qBAAqB,EAAE;IACxC,YAAY,IAAI,CAAC,sBAAsB,EAAE;IACzC,YAAY,KAAK,IAAI,CAAC,oBAAoB,CAAC;IAC3C,gBAAgB,SAAS,EAAE,aAAa;IACxC,gBAAgB,UAAU,EAAE,cAAc;IAC1C,aAAa,CAAC;IACd,YAAY,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAClE,YAAY;IACZ,QAAQ;IACR,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE;IAC9B,YAAY,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;IACnC,YAAY,IAAI,CAAC,WAAW,GAAG,IAAI;IACnC,QAAQ;IACR,QAAQ,IAAI,CAAC,wBAAwB,EAAE;IACvC,QAAQ,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC9D,IAAI;IACJ,IAAI,MAAM,WAAW,GAAG;IACxB,QAAQ,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE;IAC3C,IAAI;IACJ,IAAI,MAAM,SAAS,GAAG;IACtB,QAAQ,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;IACtC,IAAI;IACJ,IAAI,MAAM,YAAY,CAAC,OAAO,EAAE;IAChC,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE;IACzB,YAAY,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE;IAC/D,YAAY,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC;IACvD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE;IAC3D,gBAAgB,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM;IAC7D,YAAY;IACZ,QAAQ;IACR;IACA,QAAQ,IAAI,IAAI,CAAC,cAAc,EAAE;IACjC,YAAY,KAAK,IAAI,CAAC,oBAAoB,CAAC;IAC3C,gBAAgB,SAAS,EAAE,qBAAqB;IAChD,gBAAgB,UAAU,EAAE,sBAAsB;IAClD,gBAAgB,MAAM,EAAE,OAAO,CAAC,MAAM;IACtC,aAAa,CAAC;IACd,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,IAAI,UAAU,GAAG,QAAQ;IACjC,QAAQ,IAAI;IACZ,YAAY,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC;IAC7D,gBAAgB,IAAI,EAAE,YAAY;IAClC,aAAa,CAAC;IACd,YAAY,UAAU,GAAG,MAAM,CAAC,KAAK;IACrC,QAAQ;IACR,QAAQ,MAAM;IACd;IACA,QAAQ;IACR,QAAQ,IAAI,iBAAiB,GAAG,oBAAoB,EAAE,GAAG,SAAS,GAAG,eAAe;IACpF,QAAQ,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC;IAC9D,YAAY,SAAS,EAAE,2BAA2B;IAClD,YAAY,UAAU,EAAE,4BAA4B;IACpD,SAAS,CAAC;IACV,QAAQ,IAAI,aAAa,EAAE,SAAS,EAAE;IACtC,YAAY,iBAAiB,GAAG,SAAS;IACzC,QAAQ;IACR,QAAQ,OAAO;IACf,YAAY,UAAU;IACtB,YAAY,iBAAiB;IAC7B,SAAS;IACT,IAAI;IACJ,IAAI,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,IAAI;IACZ,YAAY,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACrF,YAAY,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK;IAClD,gBAAgB,KAAK,CAAC,IAAI,EAAE;IAC5B,YAAY,CAAC,CAAC;IACd,YAAY,OAAO,IAAI,CAAC,gBAAgB,EAAE;IAC1C,QAAQ;IACR,QAAQ,MAAM;IACd,YAAY,OAAO;IACnB,gBAAgB,UAAU,EAAE,QAAQ;IACpC,gBAAgB,iBAAiB,EAAE,QAAQ;IAC3C,aAAa;IACb,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,eAAe,GAAG;IAC5B,QAAQ,IAAI;IACZ,YAAY,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,gBAAgB,EAAE;IAC3E,YAAY,MAAM,WAAW,GAAG;IAChC,iBAAiB,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,YAAY;IACtD,iBAAiB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM;IAChC,gBAAgB,EAAE,EAAE,CAAC,CAAC,QAAQ;IAC9B,gBAAgB,IAAI,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,gBAAgB,SAAS,EAAE,CAAC,CAAC,QAAQ,KAAK,SAAS;IACnD,aAAa,CAAC,CAAC;IACf,YAAY,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE;IAC3C,QAAQ;IACR,QAAQ,MAAM;IACd,YAAY,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE;IAClC,QAAQ;IACR,IAAI;IACJ,IAAI,MAAM,cAAc,CAAC,QAAQ,EAAE;IACnC;IACA;IACA,QAAQ,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC;IACpH,IAAI;IACJ;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference path="../../../../electrobun/src/types/web-speech.d.ts" />
|