@quatrain/voice 1.0.1

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.
File without changes
@@ -0,0 +1,5 @@
1
+ export declare abstract class AbstractVoiceAdapter {
2
+ abstract init(): void;
3
+ abstract speak(text: string, options?: any): Promise<void>;
4
+ }
5
+ //# sourceMappingURL=AbstractVoiceAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractVoiceAdapter.d.ts","sourceRoot":"","sources":["../src/AbstractVoiceAdapter.ts"],"names":[],"mappings":"AAGA,8BAAsB,oBAAoB;IAIvC,QAAQ,CAAC,IAAI,IAAI,IAAI;IAOrB,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAC5D"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractVoiceAdapter = void 0;
4
+ class AbstractVoiceAdapter {
5
+ }
6
+ exports.AbstractVoiceAdapter = AbstractVoiceAdapter;
7
+ //# sourceMappingURL=AbstractVoiceAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractVoiceAdapter.js","sourceRoot":"","sources":["../src/AbstractVoiceAdapter.ts"],"names":[],"mappings":";;;AAGA,MAAsB,oBAAoB;CAYzC;AAZD,oDAYC"}
@@ -0,0 +1,8 @@
1
+ import { AbstractVoiceAdapter } from './AbstractVoiceAdapter';
2
+ export declare class BrowserVoiceAdapter extends AbstractVoiceAdapter {
3
+ init(): void;
4
+ speak(text: string, options?: {
5
+ lang?: string;
6
+ }): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=BrowserVoiceAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserVoiceAdapter.d.ts","sourceRoot":"","sources":["../src/BrowserVoiceAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAK7D,qBAAa,mBAAoB,SAAQ,oBAAoB;IAC1D,IAAI,IAAI,IAAI;IAUN,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAkBvE"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserVoiceAdapter = void 0;
4
+ const AbstractVoiceAdapter_1 = require("./AbstractVoiceAdapter");
5
+ class BrowserVoiceAdapter extends AbstractVoiceAdapter_1.AbstractVoiceAdapter {
6
+ init() {
7
+ }
8
+ async speak(text, options) {
9
+ if (typeof window === 'undefined' || !window.speechSynthesis) {
10
+ throw new Error('BrowserVoiceAdapter requires a browser environment with SpeechSynthesis support.');
11
+ }
12
+ window.speechSynthesis.cancel();
13
+ const cleanText = text.replace(/[#*`[\]()]/g, '');
14
+ const utterance = new SpeechSynthesisUtterance(cleanText);
15
+ if (options?.lang) {
16
+ utterance.lang = options.lang;
17
+ }
18
+ window.speechSynthesis.speak(utterance);
19
+ }
20
+ }
21
+ exports.BrowserVoiceAdapter = BrowserVoiceAdapter;
22
+ //# sourceMappingURL=BrowserVoiceAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserVoiceAdapter.js","sourceRoot":"","sources":["../src/BrowserVoiceAdapter.ts"],"names":[],"mappings":";;;AAAA,iEAA6D;AAK7D,MAAa,mBAAoB,SAAQ,2CAAoB;IAC1D,IAAI;IAEJ,CAAC;IAQD,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,OAA2B;QAClD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5D,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAA;QACtG,CAAC;QAGD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,CAAA;QAG/B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;QACjD,MAAM,SAAS,GAAG,IAAI,wBAAwB,CAAC,SAAS,CAAC,CAAA;QAEzD,IAAI,OAAO,EAAE,IAAI,EAAE,CAAC;YACjB,SAAS,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;QAChC,CAAC;QAED,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAC1C,CAAC;CACH;AA7BD,kDA6BC"}
@@ -0,0 +1,16 @@
1
+ import { AbstractVoiceAdapter } from './AbstractVoiceAdapter';
2
+ export declare class ElevenLabsVoiceAdapter extends AbstractVoiceAdapter {
3
+ private _apiKey;
4
+ private _voiceId;
5
+ private _modelId;
6
+ constructor(config: {
7
+ apiKey: string;
8
+ voiceId?: string;
9
+ modelId?: string;
10
+ });
11
+ init(): void;
12
+ speak(text: string, options?: {
13
+ voiceId?: string;
14
+ }): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=ElevenLabsVoiceAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElevenLabsVoiceAdapter.d.ts","sourceRoot":"","sources":["../src/ElevenLabsVoiceAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAK7D,qBAAa,sBAAuB,SAAQ,oBAAoB;IAC7D,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAQ;gBAEZ,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAO1E,IAAI,IAAI,IAAI;IAYN,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAiC1E"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ElevenLabsVoiceAdapter = void 0;
4
+ const AbstractVoiceAdapter_1 = require("./AbstractVoiceAdapter");
5
+ class ElevenLabsVoiceAdapter extends AbstractVoiceAdapter_1.AbstractVoiceAdapter {
6
+ _apiKey;
7
+ _voiceId;
8
+ _modelId;
9
+ constructor(config) {
10
+ super();
11
+ this._apiKey = config.apiKey;
12
+ this._voiceId = config.voiceId || 'bVsJfghVbJypxgwVISO3';
13
+ this._modelId = config.modelId || 'eleven_multilingual_v2';
14
+ }
15
+ init() {
16
+ if (!this._apiKey) {
17
+ throw new Error('ElevenLabsVoiceAdapter requires an apiKey.');
18
+ }
19
+ }
20
+ async speak(text, options) {
21
+ const voiceId = options?.voiceId || this._voiceId;
22
+ const cleanText = text.replace(/[#*`[\]()]/g, '');
23
+ const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
24
+ method: 'POST',
25
+ headers: {
26
+ 'Content-Type': 'application/json',
27
+ 'xi-api-key': this._apiKey,
28
+ },
29
+ body: JSON.stringify({
30
+ text: cleanText,
31
+ model_id: this._modelId,
32
+ voice_settings: {
33
+ stability: 0.5,
34
+ similarity_boost: 0.75,
35
+ },
36
+ }),
37
+ });
38
+ if (!response.ok) {
39
+ const errText = await response.text();
40
+ throw new Error(`ElevenLabs TTS request failed: ${response.statusText} - ${errText}`);
41
+ }
42
+ const audioBlob = await response.blob();
43
+ if (typeof window !== 'undefined') {
44
+ const audioUrl = URL.createObjectURL(audioBlob);
45
+ const audio = new Audio(audioUrl);
46
+ await audio.play();
47
+ }
48
+ }
49
+ }
50
+ exports.ElevenLabsVoiceAdapter = ElevenLabsVoiceAdapter;
51
+ //# sourceMappingURL=ElevenLabsVoiceAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElevenLabsVoiceAdapter.js","sourceRoot":"","sources":["../src/ElevenLabsVoiceAdapter.ts"],"names":[],"mappings":";;;AAAA,iEAA6D;AAK7D,MAAa,sBAAuB,SAAQ,2CAAoB;IACrD,OAAO,CAAQ;IACf,QAAQ,CAAQ;IAChB,QAAQ,CAAQ;IAExB,YAAY,MAA8D;QACvE,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAA;QAC5B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,OAAO,IAAI,sBAAsB,CAAA;QACxD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,OAAO,IAAI,wBAAwB,CAAA;IAC7D,CAAC;IAED,IAAI;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;QAChE,CAAC;IACJ,CAAC;IAQD,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,OAA8B;QACrD,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAA;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;QAEjD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,+CAA+C,OAAO,EAAE,EAAE;YACpF,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACN,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,IAAI,CAAC,OAAO;aAC5B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBAClB,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,cAAc,EAAE;oBACb,SAAS,EAAE,GAAG;oBACd,gBAAgB,EAAE,IAAI;iBACxB;aACH,CAAC;SACJ,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YACrC,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,CAAC,UAAU,MAAM,OAAO,EAAE,CAAC,CAAA;QACxF,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAEvC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAA;YAC/C,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAA;YACjC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;QACrB,CAAC;IACJ,CAAC;CACH;AAzDD,wDAyDC"}
@@ -0,0 +1,8 @@
1
+ import { AbstractVoiceAdapter } from './AbstractVoiceAdapter';
2
+ export declare class Voice {
3
+ protected static _adapter: AbstractVoiceAdapter | null;
4
+ static setAdapter(adapter: AbstractVoiceAdapter): void;
5
+ static getAdapter(): AbstractVoiceAdapter;
6
+ static speak(text: string, options?: any): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=Voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Voice.d.ts","sourceRoot":"","sources":["../src/Voice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAK7D,qBAAa,KAAK;IACf,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAO;WAO/C,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;WAW/C,UAAU,IAAI,oBAAoB;WAalC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAGjE"}
package/dist/Voice.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Voice = void 0;
4
+ class Voice {
5
+ static _adapter = null;
6
+ static setAdapter(adapter) {
7
+ this._adapter = adapter;
8
+ this._adapter.init();
9
+ }
10
+ static getAdapter() {
11
+ if (!this._adapter) {
12
+ throw new Error('Voice adapter has not been configured. Call Voice.setAdapter() first.');
13
+ }
14
+ return this._adapter;
15
+ }
16
+ static speak(text, options) {
17
+ return this.getAdapter().speak(text, options);
18
+ }
19
+ }
20
+ exports.Voice = Voice;
21
+ //# sourceMappingURL=Voice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Voice.js","sourceRoot":"","sources":["../src/Voice.ts"],"names":[],"mappings":";;;AAKA,MAAa,KAAK;IACL,MAAM,CAAC,QAAQ,GAAgC,IAAI,CAAA;IAOtD,MAAM,CAAC,UAAU,CAAC,OAA6B;QACnD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IACvB,CAAC;IAQM,MAAM,CAAC,UAAU;QACrB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAA;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAA;IACvB,CAAC;IAQM,MAAM,CAAC,KAAK,CAAC,IAAY,EAAE,OAAa;QAC5C,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChD,CAAC;;AAlCJ,sBAmCC"}
@@ -0,0 +1,5 @@
1
+ export { AbstractVoiceAdapter } from './AbstractVoiceAdapter';
2
+ export { BrowserVoiceAdapter } from './BrowserVoiceAdapter';
3
+ export { ElevenLabsVoiceAdapter } from './ElevenLabsVoiceAdapter';
4
+ export { Voice } from './Voice';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Voice = exports.ElevenLabsVoiceAdapter = exports.BrowserVoiceAdapter = exports.AbstractVoiceAdapter = void 0;
4
+ var AbstractVoiceAdapter_1 = require("./AbstractVoiceAdapter");
5
+ Object.defineProperty(exports, "AbstractVoiceAdapter", { enumerable: true, get: function () { return AbstractVoiceAdapter_1.AbstractVoiceAdapter; } });
6
+ var BrowserVoiceAdapter_1 = require("./BrowserVoiceAdapter");
7
+ Object.defineProperty(exports, "BrowserVoiceAdapter", { enumerable: true, get: function () { return BrowserVoiceAdapter_1.BrowserVoiceAdapter; } });
8
+ var ElevenLabsVoiceAdapter_1 = require("./ElevenLabsVoiceAdapter");
9
+ Object.defineProperty(exports, "ElevenLabsVoiceAdapter", { enumerable: true, get: function () { return ElevenLabsVoiceAdapter_1.ElevenLabsVoiceAdapter; } });
10
+ var Voice_1 = require("./Voice");
11
+ Object.defineProperty(exports, "Voice", { enumerable: true, get: function () { return Voice_1.Voice; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+DAA6D;AAApD,4HAAA,oBAAoB,OAAA;AAC7B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,mEAAiE;AAAxD,gIAAA,sBAAsB,OAAA;AAC/B,iCAA+B;AAAtB,8FAAA,KAAK,OAAA"}
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@quatrain/voice",
3
+ "version": "1.0.1",
4
+ "main": "src/index.ts",
5
+ "license": "AGPL-3.0-only",
6
+ "devDependencies": {
7
+ "typescript": "^5.2.2"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/Quatrain/CoreUX.git",
15
+ "directory": "packages/voice"
16
+ }
17
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Abstract blueprint for voice synthesis adapters.
3
+ */
4
+ export abstract class AbstractVoiceAdapter {
5
+ /**
6
+ * Adapter specific initialization
7
+ */
8
+ abstract init(): void
9
+
10
+ /**
11
+ * Synthesize text to speech
12
+ * @param text - The plain text to read aloud
13
+ * @param options - Configuration options such as voice ID, language, speed, etc.
14
+ */
15
+ abstract speak(text: string, options?: any): Promise<void>
16
+ }
@@ -0,0 +1,35 @@
1
+ import { AbstractVoiceAdapter } from './AbstractVoiceAdapter'
2
+
3
+ /**
4
+ * Voice synthesis adapter using the native Web Speech API (SpeechSynthesis) in the browser.
5
+ */
6
+ export class BrowserVoiceAdapter extends AbstractVoiceAdapter {
7
+ init(): void {
8
+ // Browser SpeechSynthesis does not need explicit initialization keys
9
+ }
10
+
11
+ /**
12
+ * Synthesize text using the browser's speechSynthesis engine.
13
+ *
14
+ * @param text - Plain text to synthesize.
15
+ * @param options - Configuration options. Supports `lang` (e.g. 'fr-FR', 'en-US').
16
+ */
17
+ async speak(text: string, options?: { lang?: string }): Promise<void> {
18
+ if (typeof window === 'undefined' || !window.speechSynthesis) {
19
+ throw new Error('BrowserVoiceAdapter requires a browser environment with SpeechSynthesis support.')
20
+ }
21
+
22
+ // Cancel any ongoing speech
23
+ window.speechSynthesis.cancel()
24
+
25
+ // Strip markdown syntax for natural reading
26
+ const cleanText = text.replace(/[#*`[\]()]/g, '')
27
+ const utterance = new SpeechSynthesisUtterance(cleanText)
28
+
29
+ if (options?.lang) {
30
+ utterance.lang = options.lang
31
+ }
32
+
33
+ window.speechSynthesis.speak(utterance)
34
+ }
35
+ }
@@ -0,0 +1,63 @@
1
+ import { AbstractVoiceAdapter } from './AbstractVoiceAdapter'
2
+
3
+ /**
4
+ * Voice synthesis adapter using ElevenLabs HTTP API.
5
+ */
6
+ export class ElevenLabsVoiceAdapter extends AbstractVoiceAdapter {
7
+ private _apiKey: string
8
+ private _voiceId: string
9
+ private _modelId: string
10
+
11
+ constructor(config: { apiKey: string; voiceId?: string; modelId?: string }) {
12
+ super()
13
+ this._apiKey = config.apiKey
14
+ this._voiceId = config.voiceId || 'bVsJfghVbJypxgwVISO3' // User's voice
15
+ this._modelId = config.modelId || 'eleven_multilingual_v2'
16
+ }
17
+
18
+ init(): void {
19
+ if (!this._apiKey) {
20
+ throw new Error('ElevenLabsVoiceAdapter requires an apiKey.')
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Synthesize text using ElevenLabs API and play the returned audio blob.
26
+ *
27
+ * @param text - Plain text to synthesize.
28
+ * @param options - Configuration options. Supports `voiceId` override.
29
+ */
30
+ async speak(text: string, options?: { voiceId?: string }): Promise<void> {
31
+ const voiceId = options?.voiceId || this._voiceId
32
+ const cleanText = text.replace(/[#*`[\]()]/g, '')
33
+
34
+ const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, {
35
+ method: 'POST',
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ 'xi-api-key': this._apiKey,
39
+ },
40
+ body: JSON.stringify({
41
+ text: cleanText,
42
+ model_id: this._modelId,
43
+ voice_settings: {
44
+ stability: 0.5,
45
+ similarity_boost: 0.75,
46
+ },
47
+ }),
48
+ })
49
+
50
+ if (!response.ok) {
51
+ const errText = await response.text()
52
+ throw new Error(`ElevenLabs TTS request failed: ${response.statusText} - ${errText}`)
53
+ }
54
+
55
+ const audioBlob = await response.blob()
56
+
57
+ if (typeof window !== 'undefined') {
58
+ const audioUrl = URL.createObjectURL(audioBlob)
59
+ const audio = new Audio(audioUrl)
60
+ await audio.play()
61
+ }
62
+ }
63
+ }
package/src/Voice.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { AbstractVoiceAdapter } from './AbstractVoiceAdapter'
2
+
3
+ /**
4
+ * Unified static registry to configure and trigger the active voice synthesis adapter.
5
+ */
6
+ export class Voice {
7
+ protected static _adapter: AbstractVoiceAdapter | null = null
8
+
9
+ /**
10
+ * Assign and initialize the active voice synthesis adapter.
11
+ *
12
+ * @param adapter - An instance of AbstractVoiceAdapter.
13
+ */
14
+ public static setAdapter(adapter: AbstractVoiceAdapter): void {
15
+ this._adapter = adapter
16
+ this._adapter.init()
17
+ }
18
+
19
+ /**
20
+ * Retrieve the currently active voice adapter.
21
+ *
22
+ * @returns The active AbstractVoiceAdapter.
23
+ * @throws {Error} If no adapter has been set.
24
+ */
25
+ public static getAdapter(): AbstractVoiceAdapter {
26
+ if (!this._adapter) {
27
+ throw new Error('Voice adapter has not been configured. Call Voice.setAdapter() first.')
28
+ }
29
+ return this._adapter
30
+ }
31
+
32
+ /**
33
+ * Trigger voice synthesis on the active adapter.
34
+ *
35
+ * @param text - Plain text to read.
36
+ * @param options - Configuration options for synthesis.
37
+ */
38
+ public static speak(text: string, options?: any): Promise<void> {
39
+ return this.getAdapter().speak(text, options)
40
+ }
41
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { AbstractVoiceAdapter } from './AbstractVoiceAdapter'
2
+ export { BrowserVoiceAdapter } from './BrowserVoiceAdapter'
3
+ export { ElevenLabsVoiceAdapter } from './ElevenLabsVoiceAdapter'
4
+ export { Voice } from './Voice'
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "rootDir": "src",
6
+ "outDir": "dist",
7
+ "lib": ["ES2022", "DOM"],
8
+ "paths": {}
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["node_modules", "**/dist"]
12
+ }