main
js 87 lines 2 KB
Raw
1 class SttService extends EventTarget {
2 constructor() {
3 super();
4 this.providers = new Map();
5 }
6
7 registerProvider(id, provider) {
8 if (!id || !provider) {
9 throw new Error("STT providers must define an id and provider object.");
10 }
11
12 this.providers.set(id, provider);
13 this.emitProvidersChange();
14
15 return () => this.unregisterProvider(id);
16 }
17
18 unregisterProvider(id) {
19 if (!this.providers.has(id)) return;
20 const activeProviderId = this.getActiveProviderId();
21 if (activeProviderId === id) {
22 this.stop();
23 this.emitStatusChange("inactive");
24 }
25 this.providers.delete(id);
26 this.emitProvidersChange();
27 }
28
29 getActiveProviderId() {
30 const next = this.providers.keys().next();
31 return next.done ? "" : String(next.value || "");
32 }
33
34 getActiveProvider() {
35 const providerId = this.getActiveProviderId();
36 return providerId ? this.providers.get(providerId) || null : null;
37 }
38
39 hasProvider() {
40 return !!this.getActiveProvider();
41 }
42
43 emitProvidersChange() {
44 this.dispatchEvent(
45 new CustomEvent("providerschange", {
46 detail: {
47 activeProviderId: this.getActiveProviderId(),
48 providerIds: Array.from(this.providers.keys()),
49 },
50 }),
51 );
52 }
53
54 emitStatusChange(status) {
55 this.dispatchEvent(
56 new CustomEvent("statuschange", {
57 detail: {
58 activeProviderId: this.getActiveProviderId(),
59 status,
60 },
61 }),
62 );
63 }
64
65 async handleMicrophoneClick() {
66 return await this.getActiveProvider()?.handleMicrophoneClick?.();
67 }
68
69 async requestMicrophonePermission() {
70 return await this.getActiveProvider()?.requestMicrophonePermission?.();
71 }
72
73 updateMicrophoneButtonUI() {
74 this.getActiveProvider()?.updateMicrophoneButtonUI?.();
75 }
76
77 stop() {
78 this.getActiveProvider()?.stop?.();
79 }
80
81 getStatus() {
82 return this.getActiveProvider()?.getStatus?.() || "inactive";
83 }
84 }
85
86 export const sttService = new SttService();
87 globalThis.sttService = sttService;