@elizaos/capacitor-talkmode 1.0.0 → 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 +137 -0
- package/android/build.gradle +17 -3
- package/android/src/main/java/ai/eliza/plugins/talkmode/TalkModePlugin.kt +941 -54
- package/dist/esm/definitions.d.ts +146 -0
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/web.d.ts +6 -1
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +34 -5
- 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 +137 -0
- package/dist/plugin.cjs.js +34 -5
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +34 -5
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/TalkModePlugin/TalkModePlugin.swift +266 -16
- package/package.json +18 -14
|
@@ -57,6 +57,8 @@ export interface SpeakOptions {
|
|
|
57
57
|
text: string;
|
|
58
58
|
/** Optional directive overrides */
|
|
59
59
|
directive?: TTSDirective;
|
|
60
|
+
/** Route through the on-device local-inference TTS endpoint on native mobile */
|
|
61
|
+
useLocalInferenceTts?: boolean;
|
|
60
62
|
/** Force use of system TTS */
|
|
61
63
|
useSystemTts?: boolean;
|
|
62
64
|
}
|
|
@@ -75,10 +77,51 @@ export interface SpeakResult {
|
|
|
75
77
|
/** Error message if failed */
|
|
76
78
|
error?: string;
|
|
77
79
|
}
|
|
80
|
+
export type TalkModeSessionMode = "idle" | "compose" | "push-to-talk" | "hands-free" | "passive";
|
|
81
|
+
export interface TalkModeSpeakerMetadata {
|
|
82
|
+
/** Stable app/runtime entity id for the speaker when available. */
|
|
83
|
+
entityId?: string;
|
|
84
|
+
/** Connector-native speaker id, such as a Discord user id. */
|
|
85
|
+
sourceId?: string;
|
|
86
|
+
/** Connector/source label, such as "discord", "web", or "native". */
|
|
87
|
+
source?: string;
|
|
88
|
+
/** Human-friendly display name. */
|
|
89
|
+
name?: string;
|
|
90
|
+
/** Connector username or handle. */
|
|
91
|
+
userName?: string;
|
|
92
|
+
/** Room/channel where the turn was captured. */
|
|
93
|
+
channelId?: string;
|
|
94
|
+
roomId?: string;
|
|
95
|
+
metadata?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
export interface TalkModeVoiceTurn {
|
|
98
|
+
/** Stable id for this captured speech turn when available. */
|
|
99
|
+
id?: string;
|
|
100
|
+
/** Transcript text for this turn. */
|
|
101
|
+
text: string;
|
|
102
|
+
/** Session mode that produced this turn. */
|
|
103
|
+
mode: TalkModeSessionMode;
|
|
104
|
+
/** Whether this turn is final. */
|
|
105
|
+
isFinal: boolean;
|
|
106
|
+
/** Speaker attribution when provided by the capture backend. */
|
|
107
|
+
speaker?: TalkModeSpeakerMetadata;
|
|
108
|
+
/** Capture backend/source label. */
|
|
109
|
+
source?: string;
|
|
110
|
+
/** Turn start timestamp in epoch milliseconds. */
|
|
111
|
+
startedAtMs?: number;
|
|
112
|
+
/** Turn end timestamp in epoch milliseconds. */
|
|
113
|
+
endedAtMs?: number;
|
|
114
|
+
/** STT confidence where available. */
|
|
115
|
+
confidence?: number;
|
|
116
|
+
/** Backend-specific metadata. */
|
|
117
|
+
metadata?: Record<string, unknown>;
|
|
118
|
+
}
|
|
78
119
|
/**
|
|
79
120
|
* Talk mode configuration
|
|
80
121
|
*/
|
|
81
122
|
export interface TalkModeConfig {
|
|
123
|
+
/** Session mode requested by the UI/connector. */
|
|
124
|
+
mode?: Exclude<TalkModeSessionMode, "idle">;
|
|
82
125
|
/** Session key for chat */
|
|
83
126
|
sessionKey?: string;
|
|
84
127
|
/** TTS configuration */
|
|
@@ -111,6 +154,8 @@ export interface TalkModeStateEvent {
|
|
|
111
154
|
state: TalkModeState;
|
|
112
155
|
/** Previous state */
|
|
113
156
|
previousState: TalkModeState;
|
|
157
|
+
/** Current session mode */
|
|
158
|
+
mode?: TalkModeSessionMode;
|
|
114
159
|
/** Status message */
|
|
115
160
|
statusText: string;
|
|
116
161
|
/** Whether system TTS is being used */
|
|
@@ -124,6 +169,16 @@ export interface TalkModeTranscriptEvent {
|
|
|
124
169
|
transcript: string;
|
|
125
170
|
/** Whether this is final */
|
|
126
171
|
isFinal: boolean;
|
|
172
|
+
/** Session mode that produced this transcript */
|
|
173
|
+
mode?: Exclude<TalkModeSessionMode, "idle">;
|
|
174
|
+
/** Speaker attribution when provided by the capture backend */
|
|
175
|
+
speaker?: TalkModeSpeakerMetadata;
|
|
176
|
+
/** Full turn metadata for speaker-aware clients */
|
|
177
|
+
turn?: TalkModeVoiceTurn;
|
|
178
|
+
/** Capture backend/source label */
|
|
179
|
+
source?: string;
|
|
180
|
+
/** STT confidence where available */
|
|
181
|
+
confidence?: number;
|
|
127
182
|
}
|
|
128
183
|
/**
|
|
129
184
|
* TTS start event
|
|
@@ -154,6 +209,68 @@ export interface TalkModeErrorEvent {
|
|
|
154
209
|
/** Whether recoverable */
|
|
155
210
|
recoverable: boolean;
|
|
156
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Native playback has started.
|
|
214
|
+
*/
|
|
215
|
+
export interface TalkModePlaybackStartEvent {
|
|
216
|
+
/** Playback provider that produced the audio */
|
|
217
|
+
provider: "elevenlabs" | "local-inference" | "system";
|
|
218
|
+
/** PCM sample rate when known */
|
|
219
|
+
sampleRate?: number;
|
|
220
|
+
/** PCM channel count when known */
|
|
221
|
+
channels?: number;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* One frame of raw PCM captured by the native AudioRecord diarization path.
|
|
225
|
+
*
|
|
226
|
+
* Emitted continuously while {@link TalkModePlugin.startAudioFrames} is active.
|
|
227
|
+
* `pcm16` is little-endian signed 16-bit mono PCM, base64-encoded — feed it to a
|
|
228
|
+
* JS/bun VAD / diarizer / wake-word consumer. The native STT (SpeechRecognizer /
|
|
229
|
+
* SODA) path does NOT produce these; only the explicit audio-frame mode does.
|
|
230
|
+
*/
|
|
231
|
+
export interface TalkModeAudioFrameEvent {
|
|
232
|
+
/** Base64-encoded little-endian signed 16-bit mono PCM for this frame. */
|
|
233
|
+
pcm16: string;
|
|
234
|
+
/** Sample rate of the captured PCM in Hz (e.g. 16000). */
|
|
235
|
+
sampleRate: number;
|
|
236
|
+
/** Channel count of the captured PCM (always 1 — mono). */
|
|
237
|
+
channels: 1;
|
|
238
|
+
/** Number of PCM samples in this frame (`pcm16` byte length / 2). */
|
|
239
|
+
samples: number;
|
|
240
|
+
/** Root-mean-square amplitude of this frame, normalized 0..1. */
|
|
241
|
+
rms: number;
|
|
242
|
+
/** Monotonic capture timestamp for this frame, ms (SystemClock.elapsedRealtime). */
|
|
243
|
+
timestamp: number;
|
|
244
|
+
/** Running index of this frame since capture started (0-based). */
|
|
245
|
+
frameIndex: number;
|
|
246
|
+
}
|
|
247
|
+
/** Options for {@link TalkModePlugin.startAudioFrames}. */
|
|
248
|
+
export interface AudioFrameOptions {
|
|
249
|
+
/**
|
|
250
|
+
* Target capture sample rate in Hz. Default 16000 (16 kHz mono — the rate
|
|
251
|
+
* VAD/diarizer/wake-word models expect). The device may not support every
|
|
252
|
+
* rate; the result reports the rate actually opened.
|
|
253
|
+
*/
|
|
254
|
+
sampleRate?: number;
|
|
255
|
+
/**
|
|
256
|
+
* Frames per `audioFrame` event, in milliseconds of audio. Default 20 ms
|
|
257
|
+
* (320 samples @ 16 kHz) — the standard VAD frame size.
|
|
258
|
+
*/
|
|
259
|
+
frameMs?: number;
|
|
260
|
+
}
|
|
261
|
+
/** Result of {@link TalkModePlugin.startAudioFrames}. */
|
|
262
|
+
export interface AudioFrameResult {
|
|
263
|
+
/** True when the AudioRecord capture started and frames will stream. */
|
|
264
|
+
started: boolean;
|
|
265
|
+
/** The sample rate the native AudioRecord was actually opened at, in Hz. */
|
|
266
|
+
sampleRate?: number;
|
|
267
|
+
/** Samples per emitted frame. */
|
|
268
|
+
frameSamples?: number;
|
|
269
|
+
/** True when SpeechRecognizer STT was suspended to free the mic for capture. */
|
|
270
|
+
suspendedStt?: boolean;
|
|
271
|
+
/** Populated when `started` is false. */
|
|
272
|
+
error?: string;
|
|
273
|
+
}
|
|
157
274
|
/**
|
|
158
275
|
* Permission status for talk mode
|
|
159
276
|
*/
|
|
@@ -249,6 +366,30 @@ export interface TalkModePlugin {
|
|
|
249
366
|
* @returns Promise resolving to permission status after request
|
|
250
367
|
*/
|
|
251
368
|
requestPermissions(): Promise<TalkModePermissionStatus>;
|
|
369
|
+
/**
|
|
370
|
+
* Start raw 16 kHz mono PCM frame capture (the diarization / VAD / wake-word
|
|
371
|
+
* source). Opt-in and independent of the default {@link start} STT flow.
|
|
372
|
+
*
|
|
373
|
+
* Android cannot run a parallel `AudioRecord` while `SpeechRecognizer` (SODA)
|
|
374
|
+
* holds the mic, so this SUSPENDS any active SpeechRecognizer for the duration
|
|
375
|
+
* of capture, then runs an `AudioRecord` and streams `audioFrame` events.
|
|
376
|
+
* Calling {@link stopAudioFrames} releases the `AudioRecord` and resumes STT
|
|
377
|
+
* if it was running. Native-only (no-op error on web/desktop).
|
|
378
|
+
*
|
|
379
|
+
* @returns Promise resolving to the capture result.
|
|
380
|
+
*/
|
|
381
|
+
startAudioFrames(options?: AudioFrameOptions): Promise<AudioFrameResult>;
|
|
382
|
+
/**
|
|
383
|
+
* Stop raw PCM frame capture and resume SpeechRecognizer STT if it was
|
|
384
|
+
* suspended by {@link startAudioFrames}.
|
|
385
|
+
*/
|
|
386
|
+
stopAudioFrames(): Promise<void>;
|
|
387
|
+
/**
|
|
388
|
+
* Query whether raw PCM frame capture is currently active.
|
|
389
|
+
*/
|
|
390
|
+
isCapturingAudioFrames(): Promise<{
|
|
391
|
+
capturing: boolean;
|
|
392
|
+
}>;
|
|
252
393
|
/**
|
|
253
394
|
* Add listener for state changes
|
|
254
395
|
*/
|
|
@@ -269,6 +410,11 @@ export interface TalkModePlugin {
|
|
|
269
410
|
* Add listener for errors
|
|
270
411
|
*/
|
|
271
412
|
addListener(eventName: "error", listenerFunc: (event: TalkModeErrorEvent) => void): Promise<PluginListenerHandle>;
|
|
413
|
+
addListener(eventName: "playbackStart", listenerFunc: (event: TalkModePlaybackStartEvent) => void): Promise<PluginListenerHandle>;
|
|
414
|
+
/**
|
|
415
|
+
* Add listener for raw PCM audio frames (only while startAudioFrames is active)
|
|
416
|
+
*/
|
|
417
|
+
addListener(eventName: "audioFrame", listenerFunc: (event: TalkModeAudioFrameEvent) => void): Promise<PluginListenerHandle>;
|
|
272
418
|
/**
|
|
273
419
|
* Remove all listeners
|
|
274
420
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebPlugin } from "@capacitor/core";
|
|
2
|
-
import type { SpeakOptions, SpeakResult, TalkModeConfig, TalkModePermissionStatus, TalkModeState } from "./definitions";
|
|
2
|
+
import type { AudioFrameOptions, AudioFrameResult, SpeakOptions, SpeakResult, TalkModeConfig, TalkModePermissionStatus, TalkModeState } from "./definitions";
|
|
3
3
|
/**
|
|
4
4
|
* Web implementation of TalkMode plugin
|
|
5
5
|
*
|
|
@@ -39,6 +39,11 @@ export declare class TalkModeWeb extends WebPlugin {
|
|
|
39
39
|
isSpeaking(): Promise<{
|
|
40
40
|
speaking: boolean;
|
|
41
41
|
}>;
|
|
42
|
+
startAudioFrames(_options?: AudioFrameOptions): Promise<AudioFrameResult>;
|
|
43
|
+
stopAudioFrames(): Promise<void>;
|
|
44
|
+
isCapturingAudioFrames(): Promise<{
|
|
45
|
+
capturing: boolean;
|
|
46
|
+
}>;
|
|
42
47
|
checkPermissions(): Promise<TalkModePermissionStatus>;
|
|
43
48
|
requestPermissions(): Promise<TalkModePermissionStatus>;
|
|
44
49
|
private setState;
|
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;AAO5C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,aAAa,EACd,MAAM,eAAe,CAAC;AAEvB;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,SAAS;IACxC,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,gBAAgB,CAAyC;IACjE,OAAO,CAAC,WAAW,CAA0C;IAC7D,OAAO,CAAC,OAAO,CAAS;;IASlB,KAAK,CAAC,OAAO,CAAC,EAAE;QACpB,MAAM,CAAC,EAAE,cAAc,CAAC;KACzB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAiF3C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,SAAS,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAI1C,QAAQ,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,aAAa,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAIjE,YAAY,CAAC,OAAO,EAAE;QAC1B,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIX,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IA4DlD,YAAY,IAAI,OAAO,CAAC;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IASnD,UAAU,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAI5C,gBAAgB,CACpB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,OAAO,CAAC,gBAAgB,CAAC;IAStB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC,sBAAsB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAIzD,gBAAgB,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAiCrD,kBAAkB,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAiB7D,OAAO,CAAC,QAAQ;CAWjB"}
|
package/dist/esm/web.js
CHANGED
|
@@ -43,8 +43,13 @@ export class TalkModeWeb extends WebPlugin {
|
|
|
43
43
|
this.recognition.interimResults = true;
|
|
44
44
|
this.recognition.onresult = (event) => {
|
|
45
45
|
const result = event.results[event.results.length - 1];
|
|
46
|
-
const
|
|
46
|
+
const first = result?.[0];
|
|
47
|
+
if (!first || typeof first.transcript !== "string")
|
|
48
|
+
return;
|
|
49
|
+
const transcript = first.transcript;
|
|
47
50
|
const isFinal = result.isFinal;
|
|
51
|
+
if (!transcript.trim())
|
|
52
|
+
return;
|
|
48
53
|
this.notifyListeners("transcript", { transcript, isFinal });
|
|
49
54
|
if (isFinal && transcript.trim()) {
|
|
50
55
|
// Note: Full talk mode flow would need Gateway plugin integration
|
|
@@ -122,7 +127,9 @@ export class TalkModeWeb extends WebPlugin {
|
|
|
122
127
|
// numbers in the wrong language (e.g., Chinese on a Chinese-locale system).
|
|
123
128
|
utterance.lang = options.directive?.language || "en-US";
|
|
124
129
|
// Apply directive settings if available
|
|
125
|
-
if (options.directive?.speed
|
|
130
|
+
if (typeof options.directive?.speed === "number" &&
|
|
131
|
+
Number.isFinite(options.directive.speed) &&
|
|
132
|
+
options.directive.speed > 0) {
|
|
126
133
|
utterance.rate = options.directive.speed;
|
|
127
134
|
}
|
|
128
135
|
utterance.onend = () => {
|
|
@@ -156,14 +163,32 @@ export class TalkModeWeb extends WebPlugin {
|
|
|
156
163
|
async isSpeaking() {
|
|
157
164
|
return { speaking: this.synthesis?.speaking ?? false };
|
|
158
165
|
}
|
|
166
|
+
async startAudioFrames(_options) {
|
|
167
|
+
// Raw PCM frame capture is a native-only diarization path; on web the Web
|
|
168
|
+
// Speech API gives transcripts only, with no raw-PCM hook.
|
|
169
|
+
return {
|
|
170
|
+
started: false,
|
|
171
|
+
error: "audioFrame capture is not supported on web",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async stopAudioFrames() {
|
|
175
|
+
// no-op on web
|
|
176
|
+
}
|
|
177
|
+
async isCapturingAudioFrames() {
|
|
178
|
+
return { capturing: false };
|
|
179
|
+
}
|
|
159
180
|
async checkPermissions() {
|
|
160
181
|
// Check microphone permission
|
|
161
182
|
let microphone = "prompt";
|
|
162
183
|
try {
|
|
163
|
-
const result = await navigator.permissions
|
|
184
|
+
const result = await navigator.permissions?.query?.({
|
|
164
185
|
name: "microphone",
|
|
165
186
|
});
|
|
166
|
-
|
|
187
|
+
if (result?.state === "granted" ||
|
|
188
|
+
result?.state === "denied" ||
|
|
189
|
+
result?.state === "prompt") {
|
|
190
|
+
microphone = result.state;
|
|
191
|
+
}
|
|
167
192
|
}
|
|
168
193
|
catch {
|
|
169
194
|
// Permissions API may not support microphone query
|
|
@@ -177,7 +202,11 @@ export class TalkModeWeb extends WebPlugin {
|
|
|
177
202
|
async requestPermissions() {
|
|
178
203
|
// Request microphone permission by attempting to get user media
|
|
179
204
|
try {
|
|
180
|
-
const stream = await navigator.mediaDevices
|
|
205
|
+
const stream = await navigator.mediaDevices?.getUserMedia?.({
|
|
206
|
+
audio: true,
|
|
207
|
+
});
|
|
208
|
+
if (!stream)
|
|
209
|
+
throw new Error("mediaDevices.getUserMedia unavailable");
|
|
181
210
|
stream.getTracks().forEach((track) => {
|
|
182
211
|
track.stop();
|
|
183
212
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.test.d.ts","sourceRoot":"","sources":["../../src/web.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { TalkModeWeb } from "./web";
|
|
3
|
+
class FakeRecognition extends EventTarget {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.continuous = false;
|
|
7
|
+
this.interimResults = false;
|
|
8
|
+
this.onresult = null;
|
|
9
|
+
this.onerror = null;
|
|
10
|
+
this.onend = null;
|
|
11
|
+
this.start = vi.fn();
|
|
12
|
+
this.stop = vi.fn(() => {
|
|
13
|
+
this.onend?.();
|
|
14
|
+
});
|
|
15
|
+
this.abort = vi.fn();
|
|
16
|
+
FakeRecognition.latest = this;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
FakeRecognition.latest = null;
|
|
20
|
+
class FakeUtterance {
|
|
21
|
+
constructor(text) {
|
|
22
|
+
this.text = text;
|
|
23
|
+
this.lang = "";
|
|
24
|
+
this.rate = 1;
|
|
25
|
+
this.onend = null;
|
|
26
|
+
this.onerror = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function setWindow(value) {
|
|
30
|
+
Object.defineProperty(globalThis, "window", {
|
|
31
|
+
configurable: true,
|
|
32
|
+
value,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function setNavigator(value) {
|
|
36
|
+
Object.defineProperty(globalThis, "navigator", {
|
|
37
|
+
configurable: true,
|
|
38
|
+
value,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
describe("TalkModeWeb fallback", () => {
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
vi.restoreAllMocks();
|
|
44
|
+
vi.unstubAllGlobals();
|
|
45
|
+
FakeRecognition.latest = null;
|
|
46
|
+
});
|
|
47
|
+
it("reports unsupported recognition and denied permission requests without media APIs", async () => {
|
|
48
|
+
setWindow({});
|
|
49
|
+
setNavigator({});
|
|
50
|
+
await expect(new TalkModeWeb().start()).resolves.toEqual({
|
|
51
|
+
started: false,
|
|
52
|
+
error: "Speech recognition not supported on this browser",
|
|
53
|
+
});
|
|
54
|
+
await expect(new TalkModeWeb().checkPermissions()).resolves.toEqual({
|
|
55
|
+
microphone: "prompt",
|
|
56
|
+
speechRecognition: "not_supported",
|
|
57
|
+
});
|
|
58
|
+
await expect(new TalkModeWeb().requestPermissions()).resolves.toEqual({
|
|
59
|
+
microphone: "prompt",
|
|
60
|
+
speechRecognition: "not_supported",
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
it("emits transcript events for valid recognition results and ignores malformed ones", async () => {
|
|
64
|
+
const synthesis = { cancel: vi.fn(), speak: vi.fn(), speaking: false };
|
|
65
|
+
setWindow({
|
|
66
|
+
SpeechRecognition: FakeRecognition,
|
|
67
|
+
speechSynthesis: synthesis,
|
|
68
|
+
});
|
|
69
|
+
setNavigator({});
|
|
70
|
+
const plugin = new TalkModeWeb();
|
|
71
|
+
const transcripts = vi.fn();
|
|
72
|
+
await plugin.addListener("transcript", transcripts);
|
|
73
|
+
await expect(plugin.start()).resolves.toEqual({ started: true });
|
|
74
|
+
FakeRecognition.latest?.onresult?.({
|
|
75
|
+
results: [{ isFinal: true, 0: { transcript: 42 } }],
|
|
76
|
+
});
|
|
77
|
+
expect(transcripts).not.toHaveBeenCalled();
|
|
78
|
+
FakeRecognition.latest?.onresult?.({
|
|
79
|
+
results: [{ isFinal: true, 0: { transcript: " hello " } }],
|
|
80
|
+
});
|
|
81
|
+
expect(transcripts).toHaveBeenCalledWith({
|
|
82
|
+
transcript: " hello ",
|
|
83
|
+
isFinal: true,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
it("speaks with sanitized directive values and resolves completion", async () => {
|
|
87
|
+
const utterances = [];
|
|
88
|
+
const synthesis = {
|
|
89
|
+
cancel: vi.fn(),
|
|
90
|
+
speaking: false,
|
|
91
|
+
speak: vi.fn((value) => {
|
|
92
|
+
utterances.push(value);
|
|
93
|
+
queueMicrotask(() => value.onend?.());
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
96
|
+
setWindow({ speechSynthesis: synthesis });
|
|
97
|
+
vi.stubGlobal("SpeechSynthesisUtterance", FakeUtterance);
|
|
98
|
+
const plugin = new TalkModeWeb();
|
|
99
|
+
const speaking = vi.fn();
|
|
100
|
+
const complete = vi.fn();
|
|
101
|
+
await plugin.addListener("speaking", speaking);
|
|
102
|
+
await plugin.addListener("speakComplete", complete);
|
|
103
|
+
await expect(plugin.speak({
|
|
104
|
+
text: "Hello",
|
|
105
|
+
directive: { language: "es", speed: Number.NaN },
|
|
106
|
+
})).resolves.toEqual({
|
|
107
|
+
completed: true,
|
|
108
|
+
interrupted: false,
|
|
109
|
+
usedSystemTts: true,
|
|
110
|
+
});
|
|
111
|
+
expect(synthesis.speak).toHaveBeenCalled();
|
|
112
|
+
expect(utterances[0]?.lang).toBe("es");
|
|
113
|
+
expect(utterances[0]?.rate).toBe(1);
|
|
114
|
+
expect(speaking).toHaveBeenCalledWith({
|
|
115
|
+
text: "Hello",
|
|
116
|
+
isSystemTts: true,
|
|
117
|
+
});
|
|
118
|
+
expect(complete).toHaveBeenCalledWith({ completed: true });
|
|
119
|
+
});
|
|
120
|
+
it("maps speech synthesis errors without throwing", async () => {
|
|
121
|
+
const synthesis = {
|
|
122
|
+
cancel: vi.fn(),
|
|
123
|
+
speaking: false,
|
|
124
|
+
speak: vi.fn((value) => {
|
|
125
|
+
queueMicrotask(() => value.onerror?.({ error: "interrupted" }));
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
setWindow({ speechSynthesis: synthesis });
|
|
129
|
+
vi.stubGlobal("SpeechSynthesisUtterance", FakeUtterance);
|
|
130
|
+
await expect(new TalkModeWeb().speak({ text: "Stop" })).resolves.toEqual({
|
|
131
|
+
completed: false,
|
|
132
|
+
interrupted: true,
|
|
133
|
+
usedSystemTts: true,
|
|
134
|
+
error: "interrupted",
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -51,8 +51,13 @@ class TalkModeWeb extends core.WebPlugin {
|
|
|
51
51
|
this.recognition.interimResults = true;
|
|
52
52
|
this.recognition.onresult = (event) => {
|
|
53
53
|
const result = event.results[event.results.length - 1];
|
|
54
|
-
const
|
|
54
|
+
const first = result?.[0];
|
|
55
|
+
if (!first || typeof first.transcript !== "string")
|
|
56
|
+
return;
|
|
57
|
+
const transcript = first.transcript;
|
|
55
58
|
const isFinal = result.isFinal;
|
|
59
|
+
if (!transcript.trim())
|
|
60
|
+
return;
|
|
56
61
|
this.notifyListeners("transcript", { transcript, isFinal });
|
|
57
62
|
if (isFinal && transcript.trim()) ;
|
|
58
63
|
};
|
|
@@ -127,7 +132,9 @@ class TalkModeWeb extends core.WebPlugin {
|
|
|
127
132
|
// numbers in the wrong language (e.g., Chinese on a Chinese-locale system).
|
|
128
133
|
utterance.lang = options.directive?.language || "en-US";
|
|
129
134
|
// Apply directive settings if available
|
|
130
|
-
if (options.directive?.speed
|
|
135
|
+
if (typeof options.directive?.speed === "number" &&
|
|
136
|
+
Number.isFinite(options.directive.speed) &&
|
|
137
|
+
options.directive.speed > 0) {
|
|
131
138
|
utterance.rate = options.directive.speed;
|
|
132
139
|
}
|
|
133
140
|
utterance.onend = () => {
|
|
@@ -161,14 +168,32 @@ class TalkModeWeb extends core.WebPlugin {
|
|
|
161
168
|
async isSpeaking() {
|
|
162
169
|
return { speaking: this.synthesis?.speaking ?? false };
|
|
163
170
|
}
|
|
171
|
+
async startAudioFrames(_options) {
|
|
172
|
+
// Raw PCM frame capture is a native-only diarization path; on web the Web
|
|
173
|
+
// Speech API gives transcripts only, with no raw-PCM hook.
|
|
174
|
+
return {
|
|
175
|
+
started: false,
|
|
176
|
+
error: "audioFrame capture is not supported on web",
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async stopAudioFrames() {
|
|
180
|
+
// no-op on web
|
|
181
|
+
}
|
|
182
|
+
async isCapturingAudioFrames() {
|
|
183
|
+
return { capturing: false };
|
|
184
|
+
}
|
|
164
185
|
async checkPermissions() {
|
|
165
186
|
// Check microphone permission
|
|
166
187
|
let microphone = "prompt";
|
|
167
188
|
try {
|
|
168
|
-
const result = await navigator.permissions
|
|
189
|
+
const result = await navigator.permissions?.query?.({
|
|
169
190
|
name: "microphone",
|
|
170
191
|
});
|
|
171
|
-
|
|
192
|
+
if (result?.state === "granted" ||
|
|
193
|
+
result?.state === "denied" ||
|
|
194
|
+
result?.state === "prompt") {
|
|
195
|
+
microphone = result.state;
|
|
196
|
+
}
|
|
172
197
|
}
|
|
173
198
|
catch {
|
|
174
199
|
// Permissions API may not support microphone query
|
|
@@ -182,7 +207,11 @@ class TalkModeWeb extends core.WebPlugin {
|
|
|
182
207
|
async requestPermissions() {
|
|
183
208
|
// Request microphone permission by attempting to get user media
|
|
184
209
|
try {
|
|
185
|
-
const stream = await navigator.mediaDevices
|
|
210
|
+
const stream = await navigator.mediaDevices?.getUserMedia?.({
|
|
211
|
+
audio: true,
|
|
212
|
+
});
|
|
213
|
+
if (!stream)
|
|
214
|
+
throw new Error("mediaDevices.getUserMedia unavailable");
|
|
186
215
|
stream.getTracks().forEach((track) => {
|
|
187
216
|
track.stop();
|
|
188
217
|
});
|
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.TalkModeWeb());\nexport const TalkMode = registerPlugin(\"TalkMode\", {\n web: loadWeb,\n});\n","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web implementation of TalkMode plugin\n *\n * Uses Web Speech API for TTS with limited functionality compared to native.\n * ElevenLabs streaming is not supported on web due to CORS limitations.\n */\nexport class TalkModeWeb extends WebPlugin {\n constructor() {\n super();\n this.config = {};\n this.state = \"idle\";\n this.statusText = \"Off\";\n this.synthesis = null;\n this.currentUtterance = null;\n this.recognition = null;\n this.enabled = false;\n if (typeof window !== \"undefined\" && window.speechSynthesis) {\n this.synthesis = window.speechSynthesis;\n }\n }\n async start(options) {\n if (options?.config) {\n this.config = { ...this.config, ...options.config };\n }\n // Check for Web Speech API support\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n if (!SpeechRecognitionAPI) {\n return {\n started: false,\n error: \"Speech recognition not supported on this browser\",\n };\n }\n if (!this.synthesis) {\n console.warn(\"[TalkMode] Speech synthesis not available on web\");\n }\n this.enabled = true;\n this.setState(\"listening\", \"Listening\");\n // Initialize speech recognition\n this.recognition = new SpeechRecognitionAPI();\n this.recognition.continuous = true;\n this.recognition.interimResults = true;\n this.recognition.onresult = (event) => {\n const result = event.results[event.results.length - 1];\n const transcript = result[0].transcript;\n const isFinal = result.isFinal;\n this.notifyListeners(\"transcript\", { transcript, isFinal });\n if (isFinal && transcript.trim()) {\n // Note: Full talk mode flow would need Gateway plugin integration\n // For web, we just emit the transcript\n }\n };\n this.recognition.onerror = (event) => {\n this.notifyListeners(\"error\", {\n code: event.error,\n message: event.message || event.error,\n recoverable: event.error !== \"not-allowed\",\n });\n };\n this.recognition.onend = () => {\n if (this.enabled && this.state === \"listening\") {\n // Restart recognition if still enabled\n try {\n this.recognition?.start();\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!msg.includes(\"already started\")) {\n console.warn(\"[TalkMode] Failed to restart recognition:\", msg);\n }\n }\n }\n };\n try {\n this.recognition.start();\n return { started: true };\n }\n catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to start\";\n return { started: false, error: message };\n }\n }\n async stop() {\n this.enabled = false;\n this.recognition?.stop();\n this.recognition = null;\n this.synthesis?.cancel();\n this.currentUtterance = null;\n this.setState(\"idle\", \"Off\");\n }\n async isEnabled() {\n return { enabled: this.enabled };\n }\n async getState() {\n return { state: this.state, statusText: this.statusText };\n }\n async updateConfig(options) {\n this.config = { ...this.config, ...options.config };\n }\n async speak(options) {\n if (!this.synthesis) {\n return {\n completed: false,\n interrupted: false,\n usedSystemTts: false,\n error: \"Speech synthesis not available\",\n };\n }\n // Web can only use system TTS (no ElevenLabs due to CORS)\n const text = options.text.trim();\n if (!text) {\n return { completed: true, interrupted: false, usedSystemTts: true };\n }\n this.setState(\"speaking\", \"Speaking\");\n this.notifyListeners(\"speaking\", { text, isSystemTts: true });\n return new Promise((resolve) => {\n const utterance = new SpeechSynthesisUtterance(text);\n this.currentUtterance = utterance;\n // Always set language — fallback to en-US if directive doesn't specify.\n // Without this, the browser uses the system locale, which may read\n // numbers in the wrong language (e.g., Chinese on a Chinese-locale system).\n utterance.lang = options.directive?.language || \"en-US\";\n // Apply directive settings if available\n if (options.directive?.speed) {\n utterance.rate = options.directive.speed;\n }\n utterance.onend = () => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: true });\n this.setState(\"listening\", \"Listening\");\n resolve({ completed: true, interrupted: false, usedSystemTts: true });\n };\n utterance.onerror = (event) => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: false });\n this.setState(\"idle\", \"Speech error\");\n resolve({\n completed: false,\n interrupted: event.error === \"interrupted\",\n usedSystemTts: true,\n error: event.error,\n });\n };\n this.synthesis?.speak(utterance);\n });\n }\n async stopSpeaking() {\n if (this.synthesis && this.currentUtterance) {\n this.synthesis.cancel();\n this.currentUtterance = null;\n return { interruptedAt: undefined };\n }\n return {};\n }\n async isSpeaking() {\n return { speaking: this.synthesis?.speaking ?? false };\n }\n async checkPermissions() {\n // Check microphone permission\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 API may not support microphone query\n }\n // Check if speech recognition is supported\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n const speechRecognition = SpeechRecognitionAPI ? \"prompt\" : \"not_supported\";\n return { microphone, speechRecognition };\n }\n async requestPermissions() {\n // Request microphone permission by attempting to get user media\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach((track) => {\n track.stop();\n });\n }\n catch {\n // Permission denied or error\n }\n return this.checkPermissions();\n }\n setState(state, statusText) {\n const previousState = this.state;\n this.state = state;\n this.statusText = statusText;\n this.notifyListeners(\"stateChange\", {\n state,\n previousState,\n statusText,\n usingSystemTts: true,\n });\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.TalkModeWeb());\nexport const TalkMode = registerPlugin(\"TalkMode\", {\n web: loadWeb,\n});\n","import { WebPlugin } from \"@capacitor/core\";\n/**\n * Web implementation of TalkMode plugin\n *\n * Uses Web Speech API for TTS with limited functionality compared to native.\n * ElevenLabs streaming is not supported on web due to CORS limitations.\n */\nexport class TalkModeWeb extends WebPlugin {\n constructor() {\n super();\n this.config = {};\n this.state = \"idle\";\n this.statusText = \"Off\";\n this.synthesis = null;\n this.currentUtterance = null;\n this.recognition = null;\n this.enabled = false;\n if (typeof window !== \"undefined\" && window.speechSynthesis) {\n this.synthesis = window.speechSynthesis;\n }\n }\n async start(options) {\n if (options?.config) {\n this.config = { ...this.config, ...options.config };\n }\n // Check for Web Speech API support\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n if (!SpeechRecognitionAPI) {\n return {\n started: false,\n error: \"Speech recognition not supported on this browser\",\n };\n }\n if (!this.synthesis) {\n console.warn(\"[TalkMode] Speech synthesis not available on web\");\n }\n this.enabled = true;\n this.setState(\"listening\", \"Listening\");\n // Initialize speech recognition\n this.recognition = new SpeechRecognitionAPI();\n this.recognition.continuous = true;\n this.recognition.interimResults = true;\n this.recognition.onresult = (event) => {\n const result = event.results[event.results.length - 1];\n const first = result?.[0];\n if (!first || typeof first.transcript !== \"string\")\n return;\n const transcript = first.transcript;\n const isFinal = result.isFinal;\n if (!transcript.trim())\n return;\n this.notifyListeners(\"transcript\", { transcript, isFinal });\n if (isFinal && transcript.trim()) {\n // Note: Full talk mode flow would need Gateway plugin integration\n // For web, we just emit the transcript\n }\n };\n this.recognition.onerror = (event) => {\n this.notifyListeners(\"error\", {\n code: event.error,\n message: event.message || event.error,\n recoverable: event.error !== \"not-allowed\",\n });\n };\n this.recognition.onend = () => {\n if (this.enabled && this.state === \"listening\") {\n // Restart recognition if still enabled\n try {\n this.recognition?.start();\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!msg.includes(\"already started\")) {\n console.warn(\"[TalkMode] Failed to restart recognition:\", msg);\n }\n }\n }\n };\n try {\n this.recognition.start();\n return { started: true };\n }\n catch (error) {\n const message = error instanceof Error ? error.message : \"Failed to start\";\n return { started: false, error: message };\n }\n }\n async stop() {\n this.enabled = false;\n this.recognition?.stop();\n this.recognition = null;\n this.synthesis?.cancel();\n this.currentUtterance = null;\n this.setState(\"idle\", \"Off\");\n }\n async isEnabled() {\n return { enabled: this.enabled };\n }\n async getState() {\n return { state: this.state, statusText: this.statusText };\n }\n async updateConfig(options) {\n this.config = { ...this.config, ...options.config };\n }\n async speak(options) {\n if (!this.synthesis) {\n return {\n completed: false,\n interrupted: false,\n usedSystemTts: false,\n error: \"Speech synthesis not available\",\n };\n }\n // Web can only use system TTS (no ElevenLabs due to CORS)\n const text = options.text.trim();\n if (!text) {\n return { completed: true, interrupted: false, usedSystemTts: true };\n }\n this.setState(\"speaking\", \"Speaking\");\n this.notifyListeners(\"speaking\", { text, isSystemTts: true });\n return new Promise((resolve) => {\n const utterance = new SpeechSynthesisUtterance(text);\n this.currentUtterance = utterance;\n // Always set language — fallback to en-US if directive doesn't specify.\n // Without this, the browser uses the system locale, which may read\n // numbers in the wrong language (e.g., Chinese on a Chinese-locale system).\n utterance.lang = options.directive?.language || \"en-US\";\n // Apply directive settings if available\n if (typeof options.directive?.speed === \"number\" &&\n Number.isFinite(options.directive.speed) &&\n options.directive.speed > 0) {\n utterance.rate = options.directive.speed;\n }\n utterance.onend = () => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: true });\n this.setState(\"listening\", \"Listening\");\n resolve({ completed: true, interrupted: false, usedSystemTts: true });\n };\n utterance.onerror = (event) => {\n this.currentUtterance = null;\n this.notifyListeners(\"speakComplete\", { completed: false });\n this.setState(\"idle\", \"Speech error\");\n resolve({\n completed: false,\n interrupted: event.error === \"interrupted\",\n usedSystemTts: true,\n error: event.error,\n });\n };\n this.synthesis?.speak(utterance);\n });\n }\n async stopSpeaking() {\n if (this.synthesis && this.currentUtterance) {\n this.synthesis.cancel();\n this.currentUtterance = null;\n return { interruptedAt: undefined };\n }\n return {};\n }\n async isSpeaking() {\n return { speaking: this.synthesis?.speaking ?? false };\n }\n async startAudioFrames(_options) {\n // Raw PCM frame capture is a native-only diarization path; on web the Web\n // Speech API gives transcripts only, with no raw-PCM hook.\n return {\n started: false,\n error: \"audioFrame capture is not supported on web\",\n };\n }\n async stopAudioFrames() {\n // no-op on web\n }\n async isCapturingAudioFrames() {\n return { capturing: false };\n }\n async checkPermissions() {\n // Check microphone permission\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 API may not support microphone query\n }\n // Check if speech recognition is supported\n const SpeechRecognitionAPI = window.SpeechRecognition ||\n window.webkitSpeechRecognition;\n const speechRecognition = SpeechRecognitionAPI ? \"prompt\" : \"not_supported\";\n return { microphone, speechRecognition };\n }\n async requestPermissions() {\n // Request microphone permission by attempting to get user media\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 }\n catch {\n // Permission denied or error\n }\n return this.checkPermissions();\n }\n setState(state, statusText) {\n const previousState = this.state;\n this.state = state;\n this.statusText = statusText;\n this.notifyListeners(\"stateChange\", {\n state,\n previousState,\n statusText,\n usingSystemTts: true,\n });\n }\n}\n"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
|