@elizaos/capacitor-swabble 2.0.0-beta.1 → 2.0.3-beta.2
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/LICENSE +21 -0
- package/README.md +90 -0
- package/android/build.gradle +16 -2
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +71 -17
- package/dist/esm/web.test.d.ts +2 -0
- package/dist/esm/web.test.d.ts.map +1 -0
- package/dist/esm/web.test.js +170 -0
- package/dist/plugin.cjs.js +71 -17
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +71 -17
- package/dist/plugin.js.map +1 -1
- package/package.json +14 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shaw Walters and elizaOS Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @elizaos/capacitor-swabble
|
|
2
|
+
|
|
3
|
+
Capacitor plugin for wake-word detection and live speech transcription. Integrates with Eliza agent UIs to give users a hands-free voice interface across iOS, Android, browser, and desktop (Electrobun + Whisper.cpp).
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- Listens for configurable trigger phrases ("eliza", "hey assistant", etc.) and emits a `wakeWord` event carrying the detected command text.
|
|
8
|
+
- Streams interim and final speech transcripts via a `transcript` event.
|
|
9
|
+
- Exposes microphone state changes, audio level data (for VU-meter visualizations), and errors as typed events.
|
|
10
|
+
- Manages microphone permissions across platforms.
|
|
11
|
+
|
|
12
|
+
## Platforms
|
|
13
|
+
|
|
14
|
+
| Platform | STT backend | Timing data |
|
|
15
|
+
|----------|-------------|-------------|
|
|
16
|
+
| iOS / macOS | Apple Speech framework | Yes |
|
|
17
|
+
| Android | SpeechRecognizer API | Partial |
|
|
18
|
+
| Browser | Web Speech API | No (postGap = -1) |
|
|
19
|
+
| Desktop (Electrobun) | Whisper.cpp via IPC bridge | Yes |
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @elizaos/capacitor-swabble @capacitor/core
|
|
25
|
+
npx cap sync
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
iOS requires the `Speech` and `AVFoundation` frameworks (linked automatically via the podspec). Android requires `RECORD_AUDIO` permission in your manifest.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Swabble } from "@elizaos/capacitor-swabble";
|
|
34
|
+
|
|
35
|
+
// Request microphone permission
|
|
36
|
+
await Swabble.requestPermissions();
|
|
37
|
+
|
|
38
|
+
// Listen for wake word + command
|
|
39
|
+
const handle = await Swabble.addListener("wakeWord", (event) => {
|
|
40
|
+
console.log("Wake word:", event.wakeWord);
|
|
41
|
+
console.log("Command:", event.command);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Start detection
|
|
45
|
+
await Swabble.start({
|
|
46
|
+
config: {
|
|
47
|
+
triggers: ["eliza"],
|
|
48
|
+
minCommandLength: 3,
|
|
49
|
+
locale: "en-US",
|
|
50
|
+
modelSize: "small", // Whisper model for desktop
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Stop later
|
|
55
|
+
await Swabble.stop();
|
|
56
|
+
handle.remove();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
| Option | Type | Default | Description |
|
|
62
|
+
|--------|------|---------|-------------|
|
|
63
|
+
| `triggers` | `string[]` | required | Wake phrases to detect |
|
|
64
|
+
| `minPostTriggerGap` | `number` | — | Silence (seconds) required after trigger before command (native only) |
|
|
65
|
+
| `minCommandLength` | `number` | `1` | Minimum command length in characters |
|
|
66
|
+
| `locale` | `string` | `"en-US"` | Speech recognition locale |
|
|
67
|
+
| `sampleRate` | `number` | `16000` | Audio sample rate (Hz) |
|
|
68
|
+
| `modelSize` | `"tiny"\|"base"\|"small"\|"medium"\|"large"` | — | Whisper.cpp model size (desktop only) |
|
|
69
|
+
|
|
70
|
+
## Events
|
|
71
|
+
|
|
72
|
+
| Event | Description |
|
|
73
|
+
|-------|-------------|
|
|
74
|
+
| `wakeWord` | Trigger phrase detected; carries `wakeWord`, `command`, `transcript`, `postGap`, `confidence` |
|
|
75
|
+
| `transcript` | Speech transcript update (interim and final); carries segments with timing |
|
|
76
|
+
| `stateChange` | Microphone state: `idle`, `listening`, `processing`, `error` |
|
|
77
|
+
| `audioLevel` | RMS level + peak (~10 Hz); useful for microphone visualizations |
|
|
78
|
+
| `error` | Error with `code`, `message`, and `recoverable` flag |
|
|
79
|
+
|
|
80
|
+
## Known limitations
|
|
81
|
+
|
|
82
|
+
- **Web Speech API:** `postGap`, `start`, and `duration` in transcript segments are `-1` (timing unavailable). `setAudioDevice` throws on web.
|
|
83
|
+
- **Device selection:** Only supported on native platforms; ignored or rejected on browser.
|
|
84
|
+
|
|
85
|
+
## Building
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bun run build # tsc then rollup — produces dist/esm/, dist/plugin.js, dist/plugin.cjs.js
|
|
89
|
+
bun run watch # tsc --watch (no rollup)
|
|
90
|
+
```
|
package/android/build.gradle
CHANGED
|
@@ -7,6 +7,16 @@ ext {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
apply plugin: 'com.android.library'
|
|
10
|
+
// Explicitly apply the Kotlin Android plugin. The kotlin-gradle-plugin is on
|
|
11
|
+
// the root buildscript classpath, but without applying it here AGP 8.13 falls
|
|
12
|
+
// back to its "built-in Kotlin" compile path (build/intermediates/
|
|
13
|
+
// built_in_kotlinc), which compiles the .kt sources but does NOT bundle the
|
|
14
|
+
// resulting .class files into the *release* library jar. The app's
|
|
15
|
+
// :app:assembleRelease then links a library AAR with zero plugin classes, so
|
|
16
|
+
// the Capacitor plugin (and any manifest-declared component) is absent from
|
|
17
|
+
// the release dex. Applying the standard Kotlin plugin wires Kotlin
|
|
18
|
+
// compilation into both the debug and release jar-bundling tasks.
|
|
19
|
+
apply plugin: 'org.jetbrains.kotlin.android'
|
|
10
20
|
android {
|
|
11
21
|
namespace = "ai.eliza.plugins.swabble"
|
|
12
22
|
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
|
@@ -25,8 +35,12 @@ android {
|
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
compileOptions {
|
|
28
|
-
sourceCompatibility JavaVersion.
|
|
29
|
-
targetCompatibility JavaVersion.
|
|
38
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
39
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
kotlinOptions {
|
|
43
|
+
jvmTarget = "21"
|
|
30
44
|
}
|
|
31
45
|
|
|
32
46
|
}
|
package/dist/esm/web.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EAEvB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,eAAe,CAAC;AA6JvB,qBAAa,UAAW,SAAQ,SAAS;IACvC,OAAO,CAAC,WAAW,CAA0C;IAC7D,OAAO,CAAC,MAAM,CAA8B;IAC5C,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAA+C;IAGpE,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,gBAAgB,CAAoC;IAC5D,OAAO,CAAC,mBAAmB,CAAyB;IACpD,OAAO,CAAC,cAAc,CAAS;IAE/B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,qBAAqB;YAQf,oBAAoB;IAQlC,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,qBAAqB;YAOf,uBAAuB;IA+CrC,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,sBAAsB;IAWxB,KAAK,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAkFtE,OAAO,CAAC,kBAAkB;YAyCZ,yBAAyB;IAyBvC,OAAO,CAAC,wBAAwB;IAY1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBrB,WAAW,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAI9C,SAAS,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;KAAE,CAAC;IAItD,YAAY,CAAC,OAAO,EAAE;QAC1B,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;KAChC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBX,gBAAgB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAkCpD,kBAAkB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAkBtD,eAAe,IAAI,OAAO,CAAC;QAC/B,OAAO,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KAClE,CAAC;IAiBI,cAAc,CAAC,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAOpE"}
|
package/dist/esm/web.js
CHANGED
|
@@ -30,6 +30,34 @@ function subscribeDesktopBridgeEvent(options) {
|
|
|
30
30
|
const getSpeechRecognition = () => window.SpeechRecognition ||
|
|
31
31
|
window.webkitSpeechRecognition ||
|
|
32
32
|
null;
|
|
33
|
+
function normalizeConfig(config) {
|
|
34
|
+
if (!config || !Array.isArray(config.triggers)) {
|
|
35
|
+
throw new Error("Swabble config requires a triggers array");
|
|
36
|
+
}
|
|
37
|
+
const triggers = config.triggers
|
|
38
|
+
.filter((trigger) => typeof trigger === "string")
|
|
39
|
+
.map((trigger) => trigger.trim())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
if (triggers.length === 0) {
|
|
42
|
+
throw new Error("Swabble config requires at least one non-empty trigger");
|
|
43
|
+
}
|
|
44
|
+
const minCommandLength = typeof config.minCommandLength === "number" &&
|
|
45
|
+
Number.isFinite(config.minCommandLength) &&
|
|
46
|
+
config.minCommandLength > 0
|
|
47
|
+
? Math.floor(config.minCommandLength)
|
|
48
|
+
: 1;
|
|
49
|
+
const sampleRate = typeof config.sampleRate === "number" &&
|
|
50
|
+
Number.isFinite(config.sampleRate) &&
|
|
51
|
+
config.sampleRate > 0
|
|
52
|
+
? Math.floor(config.sampleRate)
|
|
53
|
+
: 16000;
|
|
54
|
+
return {
|
|
55
|
+
...config,
|
|
56
|
+
triggers,
|
|
57
|
+
minCommandLength,
|
|
58
|
+
sampleRate,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
33
61
|
/**
|
|
34
62
|
* WakeWordGate detects trigger phrases in transcripts.
|
|
35
63
|
*
|
|
@@ -40,15 +68,22 @@ const getSpeechRecognition = () => window.SpeechRecognition ||
|
|
|
40
68
|
*/
|
|
41
69
|
class WakeWordGate {
|
|
42
70
|
constructor(config) {
|
|
43
|
-
|
|
71
|
+
const normalized = normalizeConfig(config);
|
|
72
|
+
this.triggers = normalized.triggers.map((t) => t.toLowerCase());
|
|
44
73
|
this.minCommandLength = config.minCommandLength ?? 1;
|
|
45
74
|
// Note: minPostTriggerGap cannot be enforced - Web Speech API lacks timing data
|
|
46
75
|
}
|
|
47
76
|
updateConfig(config) {
|
|
48
|
-
if (config.triggers)
|
|
49
|
-
this.triggers =
|
|
50
|
-
|
|
51
|
-
|
|
77
|
+
if (config.triggers) {
|
|
78
|
+
this.triggers = normalizeConfig({
|
|
79
|
+
triggers: config.triggers,
|
|
80
|
+
}).triggers.map((t) => t.toLowerCase());
|
|
81
|
+
}
|
|
82
|
+
if (typeof config.minCommandLength === "number" &&
|
|
83
|
+
Number.isFinite(config.minCommandLength) &&
|
|
84
|
+
config.minCommandLength > 0) {
|
|
85
|
+
this.minCommandLength = Math.floor(config.minCommandLength);
|
|
86
|
+
}
|
|
52
87
|
}
|
|
53
88
|
/**
|
|
54
89
|
* Match wake word in transcript using text-only detection.
|
|
@@ -190,6 +225,8 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
190
225
|
sink.connect(this.captureContext.destination);
|
|
191
226
|
}
|
|
192
227
|
computeRms(samples) {
|
|
228
|
+
if (samples.length === 0)
|
|
229
|
+
return 0;
|
|
193
230
|
let sum = 0;
|
|
194
231
|
for (let i = 0; i < samples.length; i++) {
|
|
195
232
|
sum += samples[i] * samples[i];
|
|
@@ -218,6 +255,7 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
218
255
|
async start(options) {
|
|
219
256
|
if (this.isActive)
|
|
220
257
|
return { started: true };
|
|
258
|
+
const config = normalizeConfig(options.config);
|
|
221
259
|
// Delegate to the native desktop bridge when available.
|
|
222
260
|
const rpc = this.getRendererRpc();
|
|
223
261
|
if (rpc) {
|
|
@@ -225,14 +263,14 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
225
263
|
const result = await this.invokeDesktopRequest({
|
|
226
264
|
rpcMethod: "swabbleStart",
|
|
227
265
|
ipcChannel: "swabble:start",
|
|
228
|
-
params: options,
|
|
266
|
+
params: { ...options, config },
|
|
229
267
|
});
|
|
230
268
|
if (result?.started) {
|
|
231
269
|
this.isActive = true;
|
|
232
270
|
this.usingNativeIpc = true;
|
|
233
|
-
this.config =
|
|
271
|
+
this.config = config;
|
|
234
272
|
this.setupNativeListeners();
|
|
235
|
-
await this.startNativeAudioCapture(
|
|
273
|
+
await this.startNativeAudioCapture(config.sampleRate ?? 16000);
|
|
236
274
|
return result;
|
|
237
275
|
}
|
|
238
276
|
}
|
|
@@ -247,13 +285,13 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
247
285
|
error: "Speech recognition not supported in this browser",
|
|
248
286
|
};
|
|
249
287
|
}
|
|
250
|
-
this.config =
|
|
251
|
-
this.wakeGate = new WakeWordGate(
|
|
288
|
+
this.config = config;
|
|
289
|
+
this.wakeGate = new WakeWordGate(config);
|
|
252
290
|
this.segments = [];
|
|
253
291
|
const recognition = new SpeechRecognitionAPI();
|
|
254
292
|
recognition.continuous = true;
|
|
255
293
|
recognition.interimResults = true;
|
|
256
|
-
recognition.lang =
|
|
294
|
+
recognition.lang = config.locale || "en-US";
|
|
257
295
|
recognition.onstart = () => {
|
|
258
296
|
this.isActive = true;
|
|
259
297
|
this.notifyListeners("stateChange", { state: "listening" });
|
|
@@ -291,10 +329,16 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
291
329
|
let transcript = "";
|
|
292
330
|
let isFinal = false;
|
|
293
331
|
for (let i = 0; i < event.results.length; i++) {
|
|
294
|
-
|
|
295
|
-
|
|
332
|
+
const result = event.results[i];
|
|
333
|
+
const first = result?.[0];
|
|
334
|
+
if (!first || typeof first.transcript !== "string")
|
|
335
|
+
continue;
|
|
336
|
+
transcript += first.transcript;
|
|
337
|
+
if (result.isFinal)
|
|
296
338
|
isFinal = true;
|
|
297
339
|
}
|
|
340
|
+
if (!transcript.trim())
|
|
341
|
+
return;
|
|
298
342
|
// Web Speech API does not provide word-level timing.
|
|
299
343
|
// Segments are provided for API compatibility but timing values are approximations.
|
|
300
344
|
const words = transcript.split(/\s+/).filter(Boolean);
|
|
@@ -401,10 +445,14 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
401
445
|
async checkPermissions() {
|
|
402
446
|
let microphone = "prompt";
|
|
403
447
|
try {
|
|
404
|
-
const result = await navigator.permissions
|
|
448
|
+
const result = await navigator.permissions?.query?.({
|
|
405
449
|
name: "microphone",
|
|
406
450
|
});
|
|
407
|
-
|
|
451
|
+
if (result?.state === "granted" ||
|
|
452
|
+
result?.state === "denied" ||
|
|
453
|
+
result?.state === "prompt") {
|
|
454
|
+
microphone = result.state;
|
|
455
|
+
}
|
|
408
456
|
}
|
|
409
457
|
catch {
|
|
410
458
|
/* permissions.query not supported for microphone in some browsers */
|
|
@@ -424,7 +472,11 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
424
472
|
}
|
|
425
473
|
async requestPermissions() {
|
|
426
474
|
try {
|
|
427
|
-
const stream = await navigator.mediaDevices
|
|
475
|
+
const stream = await navigator.mediaDevices?.getUserMedia?.({
|
|
476
|
+
audio: true,
|
|
477
|
+
});
|
|
478
|
+
if (!stream)
|
|
479
|
+
throw new Error("mediaDevices.getUserMedia unavailable");
|
|
428
480
|
stream.getTracks().forEach((track) => {
|
|
429
481
|
track.stop();
|
|
430
482
|
});
|
|
@@ -439,7 +491,9 @@ export class SwabbleWeb extends WebPlugin {
|
|
|
439
491
|
}
|
|
440
492
|
async getAudioDevices() {
|
|
441
493
|
try {
|
|
442
|
-
const devices = await navigator.mediaDevices
|
|
494
|
+
const devices = await navigator.mediaDevices?.enumerateDevices?.();
|
|
495
|
+
if (!devices)
|
|
496
|
+
return { devices: [] };
|
|
443
497
|
const audioInputs = devices
|
|
444
498
|
.filter((d) => d.kind === "audioinput")
|
|
445
499
|
.map((d, i) => ({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.test.d.ts","sourceRoot":"","sources":["../../src/web.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { SwabbleWeb } from "./web";
|
|
3
|
+
class FakeRecognition extends EventTarget {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.continuous = false;
|
|
7
|
+
this.interimResults = false;
|
|
8
|
+
this.lang = "";
|
|
9
|
+
this.onstart = null;
|
|
10
|
+
this.onend = null;
|
|
11
|
+
this.onerror = null;
|
|
12
|
+
this.onresult = null;
|
|
13
|
+
this.start = vi.fn(() => {
|
|
14
|
+
this.onstart?.();
|
|
15
|
+
});
|
|
16
|
+
this.stop = vi.fn(() => {
|
|
17
|
+
this.onend?.();
|
|
18
|
+
});
|
|
19
|
+
this.abort = vi.fn();
|
|
20
|
+
FakeRecognition.latest = this;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
FakeRecognition.latest = null;
|
|
24
|
+
function setWindow(overrides = {}) {
|
|
25
|
+
Object.defineProperty(globalThis, "window", {
|
|
26
|
+
configurable: true,
|
|
27
|
+
value: overrides,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function setNavigator(value) {
|
|
31
|
+
Object.defineProperty(globalThis, "navigator", {
|
|
32
|
+
configurable: true,
|
|
33
|
+
value,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function speechEvent(transcript, isFinal = true, confidence = 0.8) {
|
|
37
|
+
return {
|
|
38
|
+
results: [
|
|
39
|
+
{
|
|
40
|
+
isFinal,
|
|
41
|
+
0: { transcript, confidence },
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
resultIndex: 0,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
describe("SwabbleWeb fallback", () => {
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
vi.restoreAllMocks();
|
|
50
|
+
vi.unstubAllGlobals();
|
|
51
|
+
FakeRecognition.latest = null;
|
|
52
|
+
});
|
|
53
|
+
it("reports unsupported speech recognition without microphone APIs", async () => {
|
|
54
|
+
setWindow();
|
|
55
|
+
setNavigator({});
|
|
56
|
+
await expect(new SwabbleWeb().checkPermissions()).resolves.toEqual({
|
|
57
|
+
microphone: "prompt",
|
|
58
|
+
speechRecognition: "not_supported",
|
|
59
|
+
});
|
|
60
|
+
await expect(new SwabbleWeb().requestPermissions()).resolves.toEqual({
|
|
61
|
+
microphone: "denied",
|
|
62
|
+
speechRecognition: "denied",
|
|
63
|
+
});
|
|
64
|
+
await expect(new SwabbleWeb().getAudioDevices()).resolves.toEqual({
|
|
65
|
+
devices: [],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
it.each([
|
|
69
|
+
{ triggers: [] },
|
|
70
|
+
{ triggers: ["", " "] },
|
|
71
|
+
{ triggers: [123] },
|
|
72
|
+
])("rejects malformed start config %#", async (config) => {
|
|
73
|
+
setWindow({ SpeechRecognition: FakeRecognition });
|
|
74
|
+
setNavigator({});
|
|
75
|
+
await expect(new SwabbleWeb().start({ config })).rejects.toThrow("Swabble config requires");
|
|
76
|
+
expect(FakeRecognition.latest).toBeNull();
|
|
77
|
+
});
|
|
78
|
+
it("emits transcript and wake-word events from valid final speech results", async () => {
|
|
79
|
+
setWindow({ SpeechRecognition: FakeRecognition });
|
|
80
|
+
setNavigator({
|
|
81
|
+
mediaDevices: {
|
|
82
|
+
getUserMedia: vi.fn(async () => null),
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const plugin = new SwabbleWeb();
|
|
86
|
+
const states = vi.fn();
|
|
87
|
+
const transcripts = vi.fn();
|
|
88
|
+
const wakeWords = vi.fn();
|
|
89
|
+
await plugin.addListener("stateChange", states);
|
|
90
|
+
await plugin.addListener("transcript", transcripts);
|
|
91
|
+
await plugin.addListener("wakeWord", wakeWords);
|
|
92
|
+
await expect(plugin.start({
|
|
93
|
+
config: {
|
|
94
|
+
triggers: [" Eliza "],
|
|
95
|
+
minCommandLength: Number.NaN,
|
|
96
|
+
locale: "en-US",
|
|
97
|
+
},
|
|
98
|
+
})).resolves.toEqual({ started: true });
|
|
99
|
+
FakeRecognition.latest?.onresult?.(speechEvent("Eliza open calendar"));
|
|
100
|
+
expect(states).toHaveBeenCalledWith({ state: "listening" });
|
|
101
|
+
expect(transcripts).toHaveBeenCalledWith(expect.objectContaining({
|
|
102
|
+
transcript: "Eliza open calendar",
|
|
103
|
+
isFinal: true,
|
|
104
|
+
}));
|
|
105
|
+
expect(wakeWords).toHaveBeenCalledWith(expect.objectContaining({
|
|
106
|
+
wakeWord: "eliza",
|
|
107
|
+
command: "open calendar",
|
|
108
|
+
postGap: -1,
|
|
109
|
+
}));
|
|
110
|
+
});
|
|
111
|
+
it("ignores malformed speech result payloads without emitting transcripts", async () => {
|
|
112
|
+
setWindow({ SpeechRecognition: FakeRecognition });
|
|
113
|
+
setNavigator({
|
|
114
|
+
mediaDevices: {
|
|
115
|
+
getUserMedia: vi.fn(async () => null),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const plugin = new SwabbleWeb();
|
|
119
|
+
const transcripts = vi.fn();
|
|
120
|
+
await plugin.addListener("transcript", transcripts);
|
|
121
|
+
await plugin.start({ config: { triggers: ["eliza"] } });
|
|
122
|
+
FakeRecognition.latest?.onresult?.({
|
|
123
|
+
results: [{ isFinal: true, 0: { transcript: 42 } }],
|
|
124
|
+
resultIndex: 0,
|
|
125
|
+
});
|
|
126
|
+
expect(transcripts).not.toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
it("uses desktop bridge state changes and removes subscriptions on stop", async () => {
|
|
129
|
+
const listeners = new Map();
|
|
130
|
+
const swabbleStart = vi.fn(async () => ({ started: true }));
|
|
131
|
+
const swabbleStop = vi.fn(async () => undefined);
|
|
132
|
+
const onMessage = vi.fn((name, listener) => {
|
|
133
|
+
listeners.set(name, listener);
|
|
134
|
+
});
|
|
135
|
+
const offMessage = vi.fn((name) => {
|
|
136
|
+
listeners.delete(name);
|
|
137
|
+
});
|
|
138
|
+
setWindow({
|
|
139
|
+
__ELIZA_ELECTROBUN_RPC__: {
|
|
140
|
+
request: {
|
|
141
|
+
swabbleStart,
|
|
142
|
+
swabbleStop,
|
|
143
|
+
swabbleAudioChunk: vi.fn(async () => undefined),
|
|
144
|
+
},
|
|
145
|
+
onMessage,
|
|
146
|
+
offMessage,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
setNavigator({
|
|
150
|
+
mediaDevices: {
|
|
151
|
+
getUserMedia: vi.fn(async () => null),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
const plugin = new SwabbleWeb();
|
|
155
|
+
const states = vi.fn();
|
|
156
|
+
await plugin.addListener("stateChange", states);
|
|
157
|
+
await plugin.start({
|
|
158
|
+
config: { triggers: ["eliza"], sampleRate: Infinity },
|
|
159
|
+
});
|
|
160
|
+
expect(swabbleStart).toHaveBeenCalledWith({
|
|
161
|
+
config: { triggers: ["eliza"], minCommandLength: 1, sampleRate: 16000 },
|
|
162
|
+
});
|
|
163
|
+
listeners.get("swabbleStateChanged")?.({ listening: true });
|
|
164
|
+
await expect(plugin.isListening()).resolves.toEqual({ listening: true });
|
|
165
|
+
await plugin.stop();
|
|
166
|
+
expect(swabbleStop).toHaveBeenCalled();
|
|
167
|
+
expect(offMessage).toHaveBeenCalled();
|
|
168
|
+
expect(states).toHaveBeenLastCalledWith({ state: "idle" });
|
|
169
|
+
});
|
|
170
|
+
});
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -38,6 +38,34 @@ function subscribeDesktopBridgeEvent(options) {
|
|
|
38
38
|
const getSpeechRecognition = () => window.SpeechRecognition ||
|
|
39
39
|
window.webkitSpeechRecognition ||
|
|
40
40
|
null;
|
|
41
|
+
function normalizeConfig(config) {
|
|
42
|
+
if (!config || !Array.isArray(config.triggers)) {
|
|
43
|
+
throw new Error("Swabble config requires a triggers array");
|
|
44
|
+
}
|
|
45
|
+
const triggers = config.triggers
|
|
46
|
+
.filter((trigger) => typeof trigger === "string")
|
|
47
|
+
.map((trigger) => trigger.trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
if (triggers.length === 0) {
|
|
50
|
+
throw new Error("Swabble config requires at least one non-empty trigger");
|
|
51
|
+
}
|
|
52
|
+
const minCommandLength = typeof config.minCommandLength === "number" &&
|
|
53
|
+
Number.isFinite(config.minCommandLength) &&
|
|
54
|
+
config.minCommandLength > 0
|
|
55
|
+
? Math.floor(config.minCommandLength)
|
|
56
|
+
: 1;
|
|
57
|
+
const sampleRate = typeof config.sampleRate === "number" &&
|
|
58
|
+
Number.isFinite(config.sampleRate) &&
|
|
59
|
+
config.sampleRate > 0
|
|
60
|
+
? Math.floor(config.sampleRate)
|
|
61
|
+
: 16000;
|
|
62
|
+
return {
|
|
63
|
+
...config,
|
|
64
|
+
triggers,
|
|
65
|
+
minCommandLength,
|
|
66
|
+
sampleRate,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
41
69
|
/**
|
|
42
70
|
* WakeWordGate detects trigger phrases in transcripts.
|
|
43
71
|
*
|
|
@@ -48,15 +76,22 @@ const getSpeechRecognition = () => window.SpeechRecognition ||
|
|
|
48
76
|
*/
|
|
49
77
|
class WakeWordGate {
|
|
50
78
|
constructor(config) {
|
|
51
|
-
|
|
79
|
+
const normalized = normalizeConfig(config);
|
|
80
|
+
this.triggers = normalized.triggers.map((t) => t.toLowerCase());
|
|
52
81
|
this.minCommandLength = config.minCommandLength ?? 1;
|
|
53
82
|
// Note: minPostTriggerGap cannot be enforced - Web Speech API lacks timing data
|
|
54
83
|
}
|
|
55
84
|
updateConfig(config) {
|
|
56
|
-
if (config.triggers)
|
|
57
|
-
this.triggers =
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
if (config.triggers) {
|
|
86
|
+
this.triggers = normalizeConfig({
|
|
87
|
+
triggers: config.triggers,
|
|
88
|
+
}).triggers.map((t) => t.toLowerCase());
|
|
89
|
+
}
|
|
90
|
+
if (typeof config.minCommandLength === "number" &&
|
|
91
|
+
Number.isFinite(config.minCommandLength) &&
|
|
92
|
+
config.minCommandLength > 0) {
|
|
93
|
+
this.minCommandLength = Math.floor(config.minCommandLength);
|
|
94
|
+
}
|
|
60
95
|
}
|
|
61
96
|
/**
|
|
62
97
|
* Match wake word in transcript using text-only detection.
|
|
@@ -198,6 +233,8 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
198
233
|
sink.connect(this.captureContext.destination);
|
|
199
234
|
}
|
|
200
235
|
computeRms(samples) {
|
|
236
|
+
if (samples.length === 0)
|
|
237
|
+
return 0;
|
|
201
238
|
let sum = 0;
|
|
202
239
|
for (let i = 0; i < samples.length; i++) {
|
|
203
240
|
sum += samples[i] * samples[i];
|
|
@@ -226,6 +263,7 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
226
263
|
async start(options) {
|
|
227
264
|
if (this.isActive)
|
|
228
265
|
return { started: true };
|
|
266
|
+
const config = normalizeConfig(options.config);
|
|
229
267
|
// Delegate to the native desktop bridge when available.
|
|
230
268
|
const rpc = this.getRendererRpc();
|
|
231
269
|
if (rpc) {
|
|
@@ -233,14 +271,14 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
233
271
|
const result = await this.invokeDesktopRequest({
|
|
234
272
|
rpcMethod: "swabbleStart",
|
|
235
273
|
ipcChannel: "swabble:start",
|
|
236
|
-
params: options,
|
|
274
|
+
params: { ...options, config },
|
|
237
275
|
});
|
|
238
276
|
if (result?.started) {
|
|
239
277
|
this.isActive = true;
|
|
240
278
|
this.usingNativeIpc = true;
|
|
241
|
-
this.config =
|
|
279
|
+
this.config = config;
|
|
242
280
|
this.setupNativeListeners();
|
|
243
|
-
await this.startNativeAudioCapture(
|
|
281
|
+
await this.startNativeAudioCapture(config.sampleRate ?? 16000);
|
|
244
282
|
return result;
|
|
245
283
|
}
|
|
246
284
|
}
|
|
@@ -255,13 +293,13 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
255
293
|
error: "Speech recognition not supported in this browser",
|
|
256
294
|
};
|
|
257
295
|
}
|
|
258
|
-
this.config =
|
|
259
|
-
this.wakeGate = new WakeWordGate(
|
|
296
|
+
this.config = config;
|
|
297
|
+
this.wakeGate = new WakeWordGate(config);
|
|
260
298
|
this.segments = [];
|
|
261
299
|
const recognition = new SpeechRecognitionAPI();
|
|
262
300
|
recognition.continuous = true;
|
|
263
301
|
recognition.interimResults = true;
|
|
264
|
-
recognition.lang =
|
|
302
|
+
recognition.lang = config.locale || "en-US";
|
|
265
303
|
recognition.onstart = () => {
|
|
266
304
|
this.isActive = true;
|
|
267
305
|
this.notifyListeners("stateChange", { state: "listening" });
|
|
@@ -299,10 +337,16 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
299
337
|
let transcript = "";
|
|
300
338
|
let isFinal = false;
|
|
301
339
|
for (let i = 0; i < event.results.length; i++) {
|
|
302
|
-
|
|
303
|
-
|
|
340
|
+
const result = event.results[i];
|
|
341
|
+
const first = result?.[0];
|
|
342
|
+
if (!first || typeof first.transcript !== "string")
|
|
343
|
+
continue;
|
|
344
|
+
transcript += first.transcript;
|
|
345
|
+
if (result.isFinal)
|
|
304
346
|
isFinal = true;
|
|
305
347
|
}
|
|
348
|
+
if (!transcript.trim())
|
|
349
|
+
return;
|
|
306
350
|
// Web Speech API does not provide word-level timing.
|
|
307
351
|
// Segments are provided for API compatibility but timing values are approximations.
|
|
308
352
|
const words = transcript.split(/\s+/).filter(Boolean);
|
|
@@ -409,10 +453,14 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
409
453
|
async checkPermissions() {
|
|
410
454
|
let microphone = "prompt";
|
|
411
455
|
try {
|
|
412
|
-
const result = await navigator.permissions
|
|
456
|
+
const result = await navigator.permissions?.query?.({
|
|
413
457
|
name: "microphone",
|
|
414
458
|
});
|
|
415
|
-
|
|
459
|
+
if (result?.state === "granted" ||
|
|
460
|
+
result?.state === "denied" ||
|
|
461
|
+
result?.state === "prompt") {
|
|
462
|
+
microphone = result.state;
|
|
463
|
+
}
|
|
416
464
|
}
|
|
417
465
|
catch {
|
|
418
466
|
/* permissions.query not supported for microphone in some browsers */
|
|
@@ -432,7 +480,11 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
432
480
|
}
|
|
433
481
|
async requestPermissions() {
|
|
434
482
|
try {
|
|
435
|
-
const stream = await navigator.mediaDevices
|
|
483
|
+
const stream = await navigator.mediaDevices?.getUserMedia?.({
|
|
484
|
+
audio: true,
|
|
485
|
+
});
|
|
486
|
+
if (!stream)
|
|
487
|
+
throw new Error("mediaDevices.getUserMedia unavailable");
|
|
436
488
|
stream.getTracks().forEach((track) => {
|
|
437
489
|
track.stop();
|
|
438
490
|
});
|
|
@@ -447,7 +499,9 @@ class SwabbleWeb extends core.WebPlugin {
|
|
|
447
499
|
}
|
|
448
500
|
async getAudioDevices() {
|
|
449
501
|
try {
|
|
450
|
-
const devices = await navigator.mediaDevices
|
|
502
|
+
const devices = await navigator.mediaDevices?.enumerateDevices?.();
|
|
503
|
+
if (!devices)
|
|
504
|
+
return { devices: [] };
|
|
451
505
|
const audioInputs = devices
|
|
452
506
|
.filter((d) => d.kind === "audioinput")
|
|
453
507
|
.map((d, i) => ({
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.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":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.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;\nfunction normalizeConfig(config) {\n if (!config || !Array.isArray(config.triggers)) {\n throw new Error(\"Swabble config requires a triggers array\");\n }\n const triggers = config.triggers\n .filter((trigger) => typeof trigger === \"string\")\n .map((trigger) => trigger.trim())\n .filter(Boolean);\n if (triggers.length === 0) {\n throw new Error(\"Swabble config requires at least one non-empty trigger\");\n }\n const minCommandLength = typeof config.minCommandLength === \"number\" &&\n Number.isFinite(config.minCommandLength) &&\n config.minCommandLength > 0\n ? Math.floor(config.minCommandLength)\n : 1;\n const sampleRate = typeof config.sampleRate === \"number\" &&\n Number.isFinite(config.sampleRate) &&\n config.sampleRate > 0\n ? Math.floor(config.sampleRate)\n : 16000;\n return {\n ...config,\n triggers,\n minCommandLength,\n sampleRate,\n };\n}\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 const normalized = normalizeConfig(config);\n this.triggers = normalized.triggers.map((t) => t.toLowerCase());\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 = normalizeConfig({\n triggers: config.triggers,\n }).triggers.map((t) => t.toLowerCase());\n }\n if (typeof config.minCommandLength === \"number\" &&\n Number.isFinite(config.minCommandLength) &&\n config.minCommandLength > 0) {\n this.minCommandLength = Math.floor(config.minCommandLength);\n }\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 if (samples.length === 0)\n return 0;\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 const config = normalizeConfig(options.config);\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, config },\n });\n if (result?.started) {\n this.isActive = true;\n this.usingNativeIpc = true;\n this.config = config;\n this.setupNativeListeners();\n await this.startNativeAudioCapture(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 = config;\n this.wakeGate = new WakeWordGate(config);\n this.segments = [];\n const recognition = new SpeechRecognitionAPI();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = 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 const result = event.results[i];\n const first = result?.[0];\n if (!first || typeof first.transcript !== \"string\")\n continue;\n transcript += first.transcript;\n if (result.isFinal)\n isFinal = true;\n }\n if (!transcript.trim())\n return;\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 if (result?.state === \"granted\" ||\n result?.state === \"denied\" ||\n result?.state === \"prompt\") {\n microphone = result.state;\n }\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?.({\n audio: true,\n });\n if (!stream)\n throw new Error(\"mediaDevices.getUserMedia unavailable\");\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 if (!devices)\n return { devices: [] };\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":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -37,6 +37,34 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
37
37
|
const getSpeechRecognition = () => window.SpeechRecognition ||
|
|
38
38
|
window.webkitSpeechRecognition ||
|
|
39
39
|
null;
|
|
40
|
+
function normalizeConfig(config) {
|
|
41
|
+
if (!config || !Array.isArray(config.triggers)) {
|
|
42
|
+
throw new Error("Swabble config requires a triggers array");
|
|
43
|
+
}
|
|
44
|
+
const triggers = config.triggers
|
|
45
|
+
.filter((trigger) => typeof trigger === "string")
|
|
46
|
+
.map((trigger) => trigger.trim())
|
|
47
|
+
.filter(Boolean);
|
|
48
|
+
if (triggers.length === 0) {
|
|
49
|
+
throw new Error("Swabble config requires at least one non-empty trigger");
|
|
50
|
+
}
|
|
51
|
+
const minCommandLength = typeof config.minCommandLength === "number" &&
|
|
52
|
+
Number.isFinite(config.minCommandLength) &&
|
|
53
|
+
config.minCommandLength > 0
|
|
54
|
+
? Math.floor(config.minCommandLength)
|
|
55
|
+
: 1;
|
|
56
|
+
const sampleRate = typeof config.sampleRate === "number" &&
|
|
57
|
+
Number.isFinite(config.sampleRate) &&
|
|
58
|
+
config.sampleRate > 0
|
|
59
|
+
? Math.floor(config.sampleRate)
|
|
60
|
+
: 16000;
|
|
61
|
+
return {
|
|
62
|
+
...config,
|
|
63
|
+
triggers,
|
|
64
|
+
minCommandLength,
|
|
65
|
+
sampleRate,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
40
68
|
/**
|
|
41
69
|
* WakeWordGate detects trigger phrases in transcripts.
|
|
42
70
|
*
|
|
@@ -47,15 +75,22 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
47
75
|
*/
|
|
48
76
|
class WakeWordGate {
|
|
49
77
|
constructor(config) {
|
|
50
|
-
|
|
78
|
+
const normalized = normalizeConfig(config);
|
|
79
|
+
this.triggers = normalized.triggers.map((t) => t.toLowerCase());
|
|
51
80
|
this.minCommandLength = config.minCommandLength ?? 1;
|
|
52
81
|
// Note: minPostTriggerGap cannot be enforced - Web Speech API lacks timing data
|
|
53
82
|
}
|
|
54
83
|
updateConfig(config) {
|
|
55
|
-
if (config.triggers)
|
|
56
|
-
this.triggers =
|
|
57
|
-
|
|
58
|
-
|
|
84
|
+
if (config.triggers) {
|
|
85
|
+
this.triggers = normalizeConfig({
|
|
86
|
+
triggers: config.triggers,
|
|
87
|
+
}).triggers.map((t) => t.toLowerCase());
|
|
88
|
+
}
|
|
89
|
+
if (typeof config.minCommandLength === "number" &&
|
|
90
|
+
Number.isFinite(config.minCommandLength) &&
|
|
91
|
+
config.minCommandLength > 0) {
|
|
92
|
+
this.minCommandLength = Math.floor(config.minCommandLength);
|
|
93
|
+
}
|
|
59
94
|
}
|
|
60
95
|
/**
|
|
61
96
|
* Match wake word in transcript using text-only detection.
|
|
@@ -197,6 +232,8 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
197
232
|
sink.connect(this.captureContext.destination);
|
|
198
233
|
}
|
|
199
234
|
computeRms(samples) {
|
|
235
|
+
if (samples.length === 0)
|
|
236
|
+
return 0;
|
|
200
237
|
let sum = 0;
|
|
201
238
|
for (let i = 0; i < samples.length; i++) {
|
|
202
239
|
sum += samples[i] * samples[i];
|
|
@@ -225,6 +262,7 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
225
262
|
async start(options) {
|
|
226
263
|
if (this.isActive)
|
|
227
264
|
return { started: true };
|
|
265
|
+
const config = normalizeConfig(options.config);
|
|
228
266
|
// Delegate to the native desktop bridge when available.
|
|
229
267
|
const rpc = this.getRendererRpc();
|
|
230
268
|
if (rpc) {
|
|
@@ -232,14 +270,14 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
232
270
|
const result = await this.invokeDesktopRequest({
|
|
233
271
|
rpcMethod: "swabbleStart",
|
|
234
272
|
ipcChannel: "swabble:start",
|
|
235
|
-
params: options,
|
|
273
|
+
params: { ...options, config },
|
|
236
274
|
});
|
|
237
275
|
if (result?.started) {
|
|
238
276
|
this.isActive = true;
|
|
239
277
|
this.usingNativeIpc = true;
|
|
240
|
-
this.config =
|
|
278
|
+
this.config = config;
|
|
241
279
|
this.setupNativeListeners();
|
|
242
|
-
await this.startNativeAudioCapture(
|
|
280
|
+
await this.startNativeAudioCapture(config.sampleRate ?? 16000);
|
|
243
281
|
return result;
|
|
244
282
|
}
|
|
245
283
|
}
|
|
@@ -254,13 +292,13 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
254
292
|
error: "Speech recognition not supported in this browser",
|
|
255
293
|
};
|
|
256
294
|
}
|
|
257
|
-
this.config =
|
|
258
|
-
this.wakeGate = new WakeWordGate(
|
|
295
|
+
this.config = config;
|
|
296
|
+
this.wakeGate = new WakeWordGate(config);
|
|
259
297
|
this.segments = [];
|
|
260
298
|
const recognition = new SpeechRecognitionAPI();
|
|
261
299
|
recognition.continuous = true;
|
|
262
300
|
recognition.interimResults = true;
|
|
263
|
-
recognition.lang =
|
|
301
|
+
recognition.lang = config.locale || "en-US";
|
|
264
302
|
recognition.onstart = () => {
|
|
265
303
|
this.isActive = true;
|
|
266
304
|
this.notifyListeners("stateChange", { state: "listening" });
|
|
@@ -298,10 +336,16 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
298
336
|
let transcript = "";
|
|
299
337
|
let isFinal = false;
|
|
300
338
|
for (let i = 0; i < event.results.length; i++) {
|
|
301
|
-
|
|
302
|
-
|
|
339
|
+
const result = event.results[i];
|
|
340
|
+
const first = result?.[0];
|
|
341
|
+
if (!first || typeof first.transcript !== "string")
|
|
342
|
+
continue;
|
|
343
|
+
transcript += first.transcript;
|
|
344
|
+
if (result.isFinal)
|
|
303
345
|
isFinal = true;
|
|
304
346
|
}
|
|
347
|
+
if (!transcript.trim())
|
|
348
|
+
return;
|
|
305
349
|
// Web Speech API does not provide word-level timing.
|
|
306
350
|
// Segments are provided for API compatibility but timing values are approximations.
|
|
307
351
|
const words = transcript.split(/\s+/).filter(Boolean);
|
|
@@ -408,10 +452,14 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
408
452
|
async checkPermissions() {
|
|
409
453
|
let microphone = "prompt";
|
|
410
454
|
try {
|
|
411
|
-
const result = await navigator.permissions
|
|
455
|
+
const result = await navigator.permissions?.query?.({
|
|
412
456
|
name: "microphone",
|
|
413
457
|
});
|
|
414
|
-
|
|
458
|
+
if (result?.state === "granted" ||
|
|
459
|
+
result?.state === "denied" ||
|
|
460
|
+
result?.state === "prompt") {
|
|
461
|
+
microphone = result.state;
|
|
462
|
+
}
|
|
415
463
|
}
|
|
416
464
|
catch {
|
|
417
465
|
/* permissions.query not supported for microphone in some browsers */
|
|
@@ -431,7 +479,11 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
431
479
|
}
|
|
432
480
|
async requestPermissions() {
|
|
433
481
|
try {
|
|
434
|
-
const stream = await navigator.mediaDevices
|
|
482
|
+
const stream = await navigator.mediaDevices?.getUserMedia?.({
|
|
483
|
+
audio: true,
|
|
484
|
+
});
|
|
485
|
+
if (!stream)
|
|
486
|
+
throw new Error("mediaDevices.getUserMedia unavailable");
|
|
435
487
|
stream.getTracks().forEach((track) => {
|
|
436
488
|
track.stop();
|
|
437
489
|
});
|
|
@@ -446,7 +498,9 @@ var capacitorSwabble = (function (exports, core) {
|
|
|
446
498
|
}
|
|
447
499
|
async getAudioDevices() {
|
|
448
500
|
try {
|
|
449
|
-
const devices = await navigator.mediaDevices
|
|
501
|
+
const devices = await navigator.mediaDevices?.enumerateDevices?.();
|
|
502
|
+
if (!devices)
|
|
503
|
+
return { devices: [] };
|
|
450
504
|
const audioInputs = devices
|
|
451
505
|
.filter((d) => d.kind === "audioinput")
|
|
452
506
|
.map((d, i) => ({
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +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":"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"}
|
|
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;\nfunction normalizeConfig(config) {\n if (!config || !Array.isArray(config.triggers)) {\n throw new Error(\"Swabble config requires a triggers array\");\n }\n const triggers = config.triggers\n .filter((trigger) => typeof trigger === \"string\")\n .map((trigger) => trigger.trim())\n .filter(Boolean);\n if (triggers.length === 0) {\n throw new Error(\"Swabble config requires at least one non-empty trigger\");\n }\n const minCommandLength = typeof config.minCommandLength === \"number\" &&\n Number.isFinite(config.minCommandLength) &&\n config.minCommandLength > 0\n ? Math.floor(config.minCommandLength)\n : 1;\n const sampleRate = typeof config.sampleRate === \"number\" &&\n Number.isFinite(config.sampleRate) &&\n config.sampleRate > 0\n ? Math.floor(config.sampleRate)\n : 16000;\n return {\n ...config,\n triggers,\n minCommandLength,\n sampleRate,\n };\n}\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 const normalized = normalizeConfig(config);\n this.triggers = normalized.triggers.map((t) => t.toLowerCase());\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 = normalizeConfig({\n triggers: config.triggers,\n }).triggers.map((t) => t.toLowerCase());\n }\n if (typeof config.minCommandLength === \"number\" &&\n Number.isFinite(config.minCommandLength) &&\n config.minCommandLength > 0) {\n this.minCommandLength = Math.floor(config.minCommandLength);\n }\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 if (samples.length === 0)\n return 0;\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 const config = normalizeConfig(options.config);\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, config },\n });\n if (result?.started) {\n this.isActive = true;\n this.usingNativeIpc = true;\n this.config = config;\n this.setupNativeListeners();\n await this.startNativeAudioCapture(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 = config;\n this.wakeGate = new WakeWordGate(config);\n this.segments = [];\n const recognition = new SpeechRecognitionAPI();\n recognition.continuous = true;\n recognition.interimResults = true;\n recognition.lang = 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 const result = event.results[i];\n const first = result?.[0];\n if (!first || typeof first.transcript !== \"string\")\n continue;\n transcript += first.transcript;\n if (result.isFinal)\n isFinal = true;\n }\n if (!transcript.trim())\n return;\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 if (result?.state === \"granted\" ||\n result?.state === \"denied\" ||\n result?.state === \"prompt\") {\n microphone = result.state;\n }\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?.({\n audio: true,\n });\n if (!stream)\n throw new Error(\"mediaDevices.getUserMedia unavailable\");\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 if (!devices)\n return { devices: [] };\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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/capacitor-swabble",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3-beta.2",
|
|
4
4
|
"description": "Detects wake words and transcribes live speech with microphone state events.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voice",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./dist/esm/index.d.ts",
|
|
17
|
+
"bun": "./src/index.ts",
|
|
18
|
+
"development": "./src/index.ts",
|
|
17
19
|
"import": "./dist/esm/index.js",
|
|
18
20
|
"require": "./dist/plugin.cjs.js"
|
|
19
21
|
},
|
|
@@ -31,27 +33,26 @@
|
|
|
31
33
|
],
|
|
32
34
|
"author": "elizaOS",
|
|
33
35
|
"license": "MIT",
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"@elizaos/app-core": "2.0.0-beta.1"
|
|
36
|
-
},
|
|
36
|
+
"dependencies": {},
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
39
39
|
"url": "https://github.com/elizaOS/eliza"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
|
-
"build": "
|
|
43
|
-
"clean": "node
|
|
42
|
+
"build": "node ../../packages/scripts/with-package-build-lock.mjs plugins/plugin-native-swabble -- bun run build:unlocked",
|
|
43
|
+
"clean": "node ../../packages/scripts/rm-path-recursive.mjs dist",
|
|
44
|
+
"test": "vitest run",
|
|
44
45
|
"prepublishOnly": "bun run build",
|
|
45
|
-
"watch": "tsc --watch"
|
|
46
|
+
"watch": "tsc --watch",
|
|
47
|
+
"build:unlocked": "bun run clean && tsc && bunx rollup -c rollup.config.mjs"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
48
|
-
"@capacitor/android": "^8.0.0",
|
|
49
50
|
"@capacitor/core": "^8.3.1",
|
|
50
|
-
"@
|
|
51
|
+
"@elizaos/native-plugin-shared-types": "2.0.3-beta.2",
|
|
51
52
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
52
|
-
"rimraf": "^6.0.0",
|
|
53
53
|
"rollup": "^4.60.2",
|
|
54
|
-
"typescript": "^6.0.3"
|
|
54
|
+
"typescript": "^6.0.3",
|
|
55
|
+
"vitest": "^4.0.0"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"@capacitor/core": "^8.3.1"
|
|
@@ -80,5 +81,6 @@
|
|
|
80
81
|
"ios": true,
|
|
81
82
|
"android": true
|
|
82
83
|
}
|
|
83
|
-
}
|
|
84
|
+
},
|
|
85
|
+
"gitHead": "82fe0f44215954c2417328203f5bd6510985c1fc"
|
|
84
86
|
}
|