@polytts/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DengQing dengqing0821@gmail.com
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,123 @@
1
+ # @polytts/react
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@polytts/react)](https://www.npmjs.com/package/@polytts/react)
4
+
5
+ React bindings for [`polytts`](https://github.com/Dunqing/polytts) runtimes.
6
+
7
+ Use this package when you already have a runtime and want React hooks and provider state around it.
8
+
9
+ `@polytts/react` intentionally wraps a low-level `TTSRuntime`, not the higher-level `BrowserTTS` controller. In browser apps, you can use either:
10
+
11
+ - `createBrowserTTSRuntime()` directly
12
+ - `createBrowserTTS(...).runtime` if you want the simple browser controller elsewhere in your app
13
+
14
+ If you want the higher-level browser controller state in React, use `BrowserTTSProvider` and `useBrowserTTS()`.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install @polytts/react react
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ```tsx
25
+ import { createBrowserTTSRuntime } from "polytts";
26
+ import { TTSProvider, useTTS } from "@polytts/react";
27
+
28
+ const runtime = createBrowserTTSRuntime({
29
+ initialModelId: "browser-speech",
30
+ });
31
+
32
+ function SpeakButton() {
33
+ const { speak, isPreparing, isSpeaking } = useTTS();
34
+
35
+ return (
36
+ <button disabled={isPreparing || isSpeaking} onClick={() => void speak("Hello from React.")}>
37
+ Speak
38
+ </button>
39
+ );
40
+ }
41
+
42
+ export function App() {
43
+ return (
44
+ <TTSProvider runtime={runtime}>
45
+ <SpeakButton />
46
+ </TTSProvider>
47
+ );
48
+ }
49
+ ```
50
+
51
+ You can also pair it with the simple browser controller:
52
+
53
+ ```tsx
54
+ import { createBrowserTTS } from "polytts";
55
+ import { BrowserTTSProvider, useBrowserTTS } from "@polytts/react";
56
+
57
+ const tts = createBrowserTTS({
58
+ initialModelId: "browser-speech",
59
+ });
60
+
61
+ function FamilyName() {
62
+ const family = useBrowserTTS((value) => value.getSelectedFamily()?.name ?? "none");
63
+ return <span>{family}</span>;
64
+ }
65
+
66
+ export function App() {
67
+ return (
68
+ <BrowserTTSProvider tts={tts}>
69
+ <FamilyName />
70
+ </BrowserTTSProvider>
71
+ );
72
+ }
73
+ ```
74
+
75
+ ## Which provider to use
76
+
77
+ Use `TTSProvider` when:
78
+
79
+ - you already work with a `TTSRuntime`
80
+ - you want lower-level runtime state such as `isPreparing`, `phase`, and raw voice resolution
81
+ - your app owns model grouping outside `polytts`
82
+
83
+ Use `BrowserTTSProvider` when:
84
+
85
+ - you want model families, install state, and the higher-level browser controller API
86
+ - your app UI switches between families and models directly
87
+ - you want controller helpers such as `downloadModel()`, `selectFamily()`, and `isInstalled()`
88
+
89
+ ## Browser controller example
90
+
91
+ ```tsx
92
+ import { createBrowserTTS } from "polytts";
93
+ import { BrowserTTSProvider, useBrowserTTS } from "@polytts/react";
94
+
95
+ const tts = createBrowserTTS({
96
+ initialModelId: "browser-speech",
97
+ });
98
+
99
+ function DownloadState() {
100
+ const selectedModelId = useBrowserTTS((value) => value.selectedModelId);
101
+ const isInstalled = useBrowserTTS((value) =>
102
+ selectedModelId ? value.isInstalled(selectedModelId) : false,
103
+ );
104
+
105
+ return <span>{isInstalled ? "downloaded" : "not downloaded"}</span>;
106
+ }
107
+
108
+ export function App() {
109
+ return (
110
+ <BrowserTTSProvider tts={tts}>
111
+ <DownloadState />
112
+ </BrowserTTSProvider>
113
+ );
114
+ }
115
+ ```
116
+
117
+ ## SSR and lifecycle notes
118
+
119
+ - The React providers support server rendering.
120
+ - `createBrowserTTS()` and `createBrowserTTSRuntime()` are still browser entrypoints, so create them in browser/client code.
121
+ - `initialModelId` and `initialVoiceId` set the starting selection, but do not eagerly load the model.
122
+ - Some models resolve their final voices only after preparation, so a voice picker may need to wait for `ready()` or `selectModel()`.
123
+ - Preference persistence is app-owned. Store selected model, voice, and speed in your own state if you want them restored across reloads.
@@ -0,0 +1,2 @@
1
+ import { BrowserTTSContextValue, BrowserTTSController, BrowserTTSFamilyValue, BrowserTTSModelValue, BrowserTTSProvider, BrowserTTSProviderProps, BrowserTTSStateValue, TTSContextValue, TTSProvider, TTSProviderProps, useBrowserTTS, useBrowserTTSController, useBrowserTTSRuntime, useTTS, useTTSRuntime } from "./provider.js";
2
+ export { BrowserTTSContextValue, BrowserTTSController, BrowserTTSFamilyValue, BrowserTTSModelValue, BrowserTTSProvider, BrowserTTSProviderProps, BrowserTTSStateValue, TTSContextValue, TTSProvider, TTSProviderProps, useBrowserTTS, useBrowserTTSController, useBrowserTTSRuntime, useTTS, useTTSRuntime };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { BrowserTTSProvider, TTSProvider, useBrowserTTS, useBrowserTTSController, useBrowserTTSRuntime, useTTS, useTTSRuntime } from "./provider.js";
2
+ export { BrowserTTSProvider, TTSProvider, useBrowserTTS, useBrowserTTSController, useBrowserTTSRuntime, useTTS, useTTSRuntime };
@@ -0,0 +1,151 @@
1
+ import { AudioData, InstallState, ModelId, SpeakOptions, TTSRuntime, VoiceId } from "@polytts/core";
2
+ import { ReactNode } from "react";
3
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
4
+ import { BrowserTTS, BrowserTTSFamily, BrowserTTSModel, BrowserTTSSpeakOptions, BrowserTTSState } from "@polytts/browser";
5
+
6
+ //#region src/provider.d.ts
7
+ /** Context value provided by {@link TTSProvider}, exposing TTS runtime state and actions. */
8
+ interface TTSContextValue {
9
+ /** The underlying core TTS runtime. */
10
+ runtime: TTSRuntime;
11
+ /** All registered model specs. */
12
+ models: ReturnType<TTSRuntime["listModels"]>;
13
+ /** IDs of models supported by the current environment. */
14
+ supportedModelIds: string[];
15
+ activeModelId: string | null;
16
+ activeVoiceId: string | null;
17
+ /** Voices available for the active model. */
18
+ voices: Awaited<ReturnType<TTSRuntime["listVoices"]>>;
19
+ /** True while a model is being downloaded or loaded. */
20
+ isPreparing: boolean;
21
+ /** True while audio is being played. */
22
+ isSpeaking: boolean;
23
+ /** Granular runtime phase (e.g. installing, loading, speaking). */
24
+ phase: ReturnType<TTSRuntime["getState"]>["phase"];
25
+ /** Model that the current phase applies to, if any. */
26
+ phaseModelId: ReturnType<TTSRuntime["getState"]>["phaseModelId"];
27
+ /** Progress of the current phase from 0 to 1, or null. */
28
+ phaseProgress: ReturnType<TTSRuntime["getState"]>["phaseProgress"];
29
+ /** Per-model runtime information (e.g. WebGPU availability). */
30
+ runtimeInfoByModel: ReturnType<TTSRuntime["getState"]>["runtimeInfoByModel"];
31
+ error: string | null;
32
+ /** Per-model install/download states. */
33
+ installStates: ReturnType<TTSRuntime["getState"]>["installStates"];
34
+ /** Current playback speed multiplier. */
35
+ selectedSpeed: number;
36
+ /** Updates the playback speed multiplier. */
37
+ setSpeed: (speed: number) => void;
38
+ /** Activates a voice on the current model. */
39
+ setVoice: (voiceId: VoiceId) => Promise<void>;
40
+ /** Loads a model (and optionally a voice) so it is ready to speak. */
41
+ prepareModel: (modelId: ModelId, voiceId?: VoiceId) => Promise<void>;
42
+ /** Downloads model assets, with optional progress callback. */
43
+ installModel: (modelId: ModelId, onProgress?: (progress: number) => void) => Promise<void>;
44
+ /** Removes previously downloaded model assets. */
45
+ uninstallModel: (modelId: ModelId) => Promise<void>;
46
+ /** Synthesizes and plays text, using the selected speed. */
47
+ speak: (text: string, options?: SpeakOptions) => Promise<void>;
48
+ /** Returns an async iterable of audio chunks for streaming playback. */
49
+ synthesizeStream: (text: string, options?: SpeakOptions) => AsyncIterable<AudioData>;
50
+ /** Synthesizes text and returns the complete audio data. */
51
+ synthesize: (text: string, options?: SpeakOptions) => Promise<AudioData>;
52
+ /** Stops any in-progress playback. */
53
+ stop: () => void;
54
+ }
55
+ /** Re-export of the underlying BrowserTTS controller for direct access. */
56
+ type BrowserTTSController = BrowserTTS;
57
+ /** Re-export of the BrowserTTSModel type for convenience. */
58
+ type BrowserTTSModelValue = BrowserTTSModel;
59
+ /** Re-export of the BrowserTTSFamily type for convenience. */
60
+ type BrowserTTSFamilyValue = BrowserTTSFamily;
61
+ /** Re-export of the BrowserTTSState type for convenience. */
62
+ type BrowserTTSStateValue = BrowserTTSState;
63
+ /** Context value provided by {@link BrowserTTSProvider}, exposing browser TTS state and actions. */
64
+ interface BrowserTTSContextValue extends BrowserTTSState {
65
+ /** The underlying BrowserTTS controller. */
66
+ tts: BrowserTTS;
67
+ /** The underlying core TTS runtime. */
68
+ runtime: TTSRuntime;
69
+ /** Current playback speed multiplier. */
70
+ selectedSpeed: number;
71
+ /** Updates the playback speed multiplier. */
72
+ setSpeed: (speed: number) => void;
73
+ /** Returns the install state for the given or active model. */
74
+ getInstallState: (modelId?: ModelId | null) => InstallState | null;
75
+ /** Returns true if the given or active model's assets are fully available. */
76
+ isInstalled: (modelId?: ModelId | null) => boolean;
77
+ /** Activates a model, downloading assets if necessary. */
78
+ selectModel: BrowserTTS["selectModel"];
79
+ /** Activates a model family, picking the best supported variant. */
80
+ selectFamily: BrowserTTS["selectFamily"];
81
+ /** Activates a voice, resolving its parent model automatically. */
82
+ setVoice: BrowserTTS["selectVoice"];
83
+ /** Ensures the selected model is loaded and ready to speak. */
84
+ ready: BrowserTTS["ready"];
85
+ /** Downloads model assets without activating the model. */
86
+ downloadModel: BrowserTTS["download"];
87
+ /** Removes previously downloaded model assets. */
88
+ removeDownload: BrowserTTS["removeDownload"];
89
+ /** Synthesizes and plays text, using the selected speed. */
90
+ speak: (text: string, options?: BrowserTTSSpeakOptions) => Promise<void>;
91
+ /** Returns an async iterable of audio chunks for streaming playback. */
92
+ synthesizeStream: (text: string, options?: BrowserTTSSpeakOptions) => AsyncIterable<AudioData>;
93
+ /** Synthesizes text and returns the complete audio data. */
94
+ synthesize: (text: string, options?: BrowserTTSSpeakOptions) => Promise<AudioData>;
95
+ /** Stops any in-progress playback. */
96
+ stop: () => void;
97
+ }
98
+ /** Props for the {@link TTSProvider} component. */
99
+ interface TTSProviderProps {
100
+ runtime: TTSRuntime;
101
+ children: ReactNode;
102
+ /** Starting playback speed multiplier (defaults to 1). */
103
+ initialSpeed?: number;
104
+ }
105
+ /** Props for the {@link BrowserTTSProvider} component. */
106
+ interface BrowserTTSProviderProps {
107
+ /** The BrowserTTS instance to expose via context. */
108
+ tts: BrowserTTS;
109
+ children: ReactNode;
110
+ /** Starting playback speed multiplier (defaults to 1). */
111
+ initialSpeed?: number;
112
+ }
113
+ /**
114
+ * React context provider that wraps a TTSRuntime and exposes TTS state and actions to descendants.
115
+ * Use with {@link useTTS} or {@link useTTSRuntime} to consume the context.
116
+ */
117
+ declare function TTSProvider({
118
+ runtime,
119
+ children,
120
+ initialSpeed
121
+ }: TTSProviderProps): _$react_jsx_runtime0.JSX.Element;
122
+ /**
123
+ * React context provider for browser-based TTS, wrapping a BrowserTTS instance. Use with
124
+ * {@link useBrowserTTS}, {@link useBrowserTTSRuntime}, or {@link useBrowserTTSController} to consume
125
+ * the context.
126
+ */
127
+ declare function BrowserTTSProvider({
128
+ tts,
129
+ children,
130
+ initialSpeed
131
+ }: BrowserTTSProviderProps): _$react_jsx_runtime0.JSX.Element;
132
+ /**
133
+ * Returns the full TTS context value, or a selected slice of it via an optional selector. Must be
134
+ * used within a {@link TTSProvider}. Use a selector to avoid unnecessary re-renders.
135
+ */
136
+ declare function useTTS(): TTSContextValue;
137
+ declare function useTTS<T>(selector: (value: TTSContextValue) => T): T;
138
+ /** Returns the underlying TTSRuntime from the nearest {@link TTSProvider}. */
139
+ declare function useTTSRuntime(): TTSRuntime;
140
+ /**
141
+ * Returns the full browser TTS context value, or a selected slice via an optional selector. Must be
142
+ * used within a {@link BrowserTTSProvider}. Use a selector to avoid unnecessary re-renders.
143
+ */
144
+ declare function useBrowserTTS(): BrowserTTSContextValue;
145
+ declare function useBrowserTTS<T>(selector: (value: BrowserTTSContextValue) => T): T;
146
+ /** Returns the underlying TTSRuntime from the nearest {@link BrowserTTSProvider}. */
147
+ declare function useBrowserTTSRuntime(): TTSRuntime;
148
+ /** Returns the underlying BrowserTTS controller from the nearest {@link BrowserTTSProvider}. */
149
+ declare function useBrowserTTSController(): BrowserTTSController;
150
+ //#endregion
151
+ export { BrowserTTSContextValue, BrowserTTSController, BrowserTTSFamilyValue, BrowserTTSModelValue, BrowserTTSProvider, BrowserTTSProviderProps, BrowserTTSStateValue, TTSContextValue, TTSProvider, TTSProviderProps, useBrowserTTS, useBrowserTTSController, useBrowserTTSRuntime, useTTS, useTTSRuntime };
@@ -0,0 +1,214 @@
1
+ import { normalizeSpeakSpeed } from "@polytts/core";
2
+ import { createContext, useCallback, useContext, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/provider.tsx
5
+ const TTSContext = createContext(null);
6
+ const BrowserTTSContext = createContext(null);
7
+ /**
8
+ * React context provider that wraps a TTSRuntime and exposes TTS state and actions to descendants.
9
+ * Use with {@link useTTS} or {@link useTTSRuntime} to consume the context.
10
+ */
11
+ function TTSProvider({ runtime, children, initialSpeed }) {
12
+ const runtimeState = useSyncExternalStore(runtime.subscribe.bind(runtime), runtime.getState.bind(runtime), runtime.getState.bind(runtime));
13
+ const [selectedSpeed, setSelectedSpeedState] = useState(() => normalizeSpeakSpeed(initialSpeed));
14
+ const setSpeed = useCallback((speed) => {
15
+ setSelectedSpeedState(normalizeSpeakSpeed(speed));
16
+ }, []);
17
+ const prepareModel = useCallback(async (modelId, voiceId) => {
18
+ await runtime.prepare(modelId, { voiceId });
19
+ }, [runtime]);
20
+ const setVoice = useCallback(async (voiceId) => {
21
+ const activeModelId = runtime.getState().activeModelId;
22
+ if (!activeModelId) return;
23
+ await runtime.prepare(activeModelId, { voiceId });
24
+ }, [runtime]);
25
+ const installModel = useCallback(async (modelId, onProgress) => {
26
+ await runtime.install(modelId, onProgress);
27
+ }, [runtime]);
28
+ const uninstallModel = useCallback(async (modelId) => {
29
+ await runtime.uninstall(modelId);
30
+ }, [runtime]);
31
+ const speak = useCallback(async (text, options) => {
32
+ await runtime.speak(text, {
33
+ ...options,
34
+ voiceId: options?.voiceId ?? runtime.getState().activeVoiceId ?? void 0,
35
+ speed: options?.speed ?? selectedSpeed
36
+ });
37
+ }, [runtime, selectedSpeed]);
38
+ const synthesize = useCallback(async (text, options) => {
39
+ return runtime.synthesize(text, {
40
+ ...options,
41
+ voiceId: options?.voiceId ?? runtime.getState().activeVoiceId ?? void 0,
42
+ speed: options?.speed ?? selectedSpeed
43
+ });
44
+ }, [runtime, selectedSpeed]);
45
+ const synthesizeStream = useCallback((text, options) => runtime.synthesizeStream(text, {
46
+ ...options,
47
+ voiceId: options?.voiceId ?? runtime.getState().activeVoiceId ?? void 0,
48
+ speed: options?.speed ?? selectedSpeed
49
+ }), [runtime, selectedSpeed]);
50
+ const value = useMemo(() => ({
51
+ runtime,
52
+ models: runtime.listModels(),
53
+ supportedModelIds: runtimeState.supportedModelIds,
54
+ activeModelId: runtimeState.activeModelId,
55
+ activeVoiceId: runtimeState.activeVoiceId,
56
+ voices: runtimeState.voices,
57
+ isPreparing: runtimeState.isPreparing,
58
+ isSpeaking: runtimeState.isSpeaking,
59
+ phase: runtimeState.phase,
60
+ phaseModelId: runtimeState.phaseModelId,
61
+ phaseProgress: runtimeState.phaseProgress,
62
+ runtimeInfoByModel: runtimeState.runtimeInfoByModel,
63
+ error: runtimeState.error,
64
+ installStates: runtimeState.installStates,
65
+ selectedSpeed,
66
+ setSpeed,
67
+ setVoice,
68
+ prepareModel,
69
+ installModel,
70
+ uninstallModel,
71
+ speak,
72
+ synthesizeStream,
73
+ synthesize,
74
+ stop: runtime.stop.bind(runtime)
75
+ }), [
76
+ installModel,
77
+ prepareModel,
78
+ runtime,
79
+ runtimeState,
80
+ selectedSpeed,
81
+ setSpeed,
82
+ setVoice,
83
+ speak,
84
+ synthesize,
85
+ synthesizeStream,
86
+ uninstallModel
87
+ ]);
88
+ return /* @__PURE__ */ jsx(TTSContext.Provider, {
89
+ value,
90
+ children
91
+ });
92
+ }
93
+ /**
94
+ * React context provider for browser-based TTS, wrapping a BrowserTTS instance. Use with
95
+ * {@link useBrowserTTS}, {@link useBrowserTTSRuntime}, or {@link useBrowserTTSController} to consume
96
+ * the context.
97
+ */
98
+ function BrowserTTSProvider({ tts, children, initialSpeed }) {
99
+ const browserStore = useMemo(() => {
100
+ let snapshot = tts.getState();
101
+ return {
102
+ subscribe(listener) {
103
+ return tts.subscribe((state) => {
104
+ snapshot = state;
105
+ listener();
106
+ });
107
+ },
108
+ getSnapshot() {
109
+ return snapshot;
110
+ },
111
+ getServerSnapshot() {
112
+ return snapshot;
113
+ }
114
+ };
115
+ }, [tts]);
116
+ const browserState = useSyncExternalStore(browserStore.subscribe.bind(browserStore), browserStore.getSnapshot.bind(browserStore), browserStore.getServerSnapshot.bind(browserStore));
117
+ const [selectedSpeed, setSelectedSpeedState] = useState(() => normalizeSpeakSpeed(initialSpeed));
118
+ const setSpeed = useCallback((speed) => {
119
+ setSelectedSpeedState(normalizeSpeakSpeed(speed));
120
+ }, []);
121
+ const speak = useCallback(async (text, options) => {
122
+ await tts.speak(text, {
123
+ ...options,
124
+ voiceId: options?.voiceId ?? browserState.selectedVoiceId ?? void 0,
125
+ speed: options?.speed ?? selectedSpeed
126
+ });
127
+ }, [
128
+ browserState.selectedVoiceId,
129
+ selectedSpeed,
130
+ tts
131
+ ]);
132
+ const synthesize = useCallback(async (text, options) => {
133
+ return tts.synthesize(text, {
134
+ ...options,
135
+ voiceId: options?.voiceId ?? browserState.selectedVoiceId ?? void 0,
136
+ speed: options?.speed ?? selectedSpeed
137
+ });
138
+ }, [
139
+ browserState.selectedVoiceId,
140
+ selectedSpeed,
141
+ tts
142
+ ]);
143
+ const synthesizeStream = useCallback((text, options) => tts.synthesizeStream(text, {
144
+ ...options,
145
+ voiceId: options?.voiceId ?? browserState.selectedVoiceId ?? void 0,
146
+ speed: options?.speed ?? selectedSpeed
147
+ }), [
148
+ browserState.selectedVoiceId,
149
+ selectedSpeed,
150
+ tts
151
+ ]);
152
+ const value = useMemo(() => ({
153
+ tts,
154
+ runtime: tts.runtime,
155
+ ...browserState,
156
+ selectedSpeed,
157
+ setSpeed,
158
+ getInstallState: tts.getInstallState.bind(tts),
159
+ isInstalled: tts.isInstalled.bind(tts),
160
+ selectModel: tts.selectModel.bind(tts),
161
+ selectFamily: tts.selectFamily.bind(tts),
162
+ setVoice: tts.selectVoice.bind(tts),
163
+ ready: tts.ready.bind(tts),
164
+ downloadModel: tts.download.bind(tts),
165
+ removeDownload: tts.removeDownload.bind(tts),
166
+ speak,
167
+ synthesizeStream,
168
+ synthesize,
169
+ stop: tts.stop.bind(tts)
170
+ }), [
171
+ browserState,
172
+ selectedSpeed,
173
+ setSpeed,
174
+ speak,
175
+ synthesize,
176
+ synthesizeStream,
177
+ tts
178
+ ]);
179
+ return /* @__PURE__ */ jsx(BrowserTTSContext.Provider, {
180
+ value,
181
+ children
182
+ });
183
+ }
184
+ function useStableSelector(context, selector) {
185
+ const selectedRef = useRef(void 0);
186
+ const selected = selector(context);
187
+ if (!Object.is(selectedRef.current, selected)) selectedRef.current = selected;
188
+ return selectedRef.current;
189
+ }
190
+ const identity = (v) => v;
191
+ function useTTS(selector) {
192
+ const context = useContext(TTSContext);
193
+ if (!context) throw new Error("useTTS must be used within a TTSProvider");
194
+ return useStableSelector(context, selector ?? identity);
195
+ }
196
+ /** Returns the underlying TTSRuntime from the nearest {@link TTSProvider}. */
197
+ function useTTSRuntime() {
198
+ return useTTS((value) => value.runtime);
199
+ }
200
+ function useBrowserTTS(selector) {
201
+ const context = useContext(BrowserTTSContext);
202
+ if (!context) throw new Error("useBrowserTTS must be used within a BrowserTTSProvider");
203
+ return useStableSelector(context, selector ?? identity);
204
+ }
205
+ /** Returns the underlying TTSRuntime from the nearest {@link BrowserTTSProvider}. */
206
+ function useBrowserTTSRuntime() {
207
+ return useBrowserTTS((value) => value.runtime);
208
+ }
209
+ /** Returns the underlying BrowserTTS controller from the nearest {@link BrowserTTSProvider}. */
210
+ function useBrowserTTSController() {
211
+ return useBrowserTTS((value) => value.tts);
212
+ }
213
+ //#endregion
214
+ export { BrowserTTSProvider, TTSProvider, useBrowserTTS, useBrowserTTSController, useBrowserTTSRuntime, useTTS, useTTSRuntime };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@polytts/react",
3
+ "version": "0.1.0",
4
+ "description": "React bindings for polytts runtimes.",
5
+ "keywords": [
6
+ "polytts",
7
+ "react",
8
+ "text-to-speech",
9
+ "tts"
10
+ ],
11
+ "homepage": "https://github.com/Dunqing/polytts/tree/main/packages/react#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/Dunqing/polytts/issues"
14
+ },
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Dunqing/polytts.git",
19
+ "directory": "packages/react"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "dependencies": {
36
+ "@polytts/browser": "0.1.0",
37
+ "@polytts/core": "0.1.0"
38
+ },
39
+ "devDependencies": {
40
+ "@testing-library/react": "latest",
41
+ "@types/react": "latest",
42
+ "@types/react-dom": "latest",
43
+ "jsdom": "latest",
44
+ "react": "latest",
45
+ "react-dom": "latest",
46
+ "vite-plus": "latest"
47
+ },
48
+ "peerDependencies": {
49
+ "react": ">=19"
50
+ },
51
+ "scripts": {
52
+ "build": "vp pack",
53
+ "test": "vp test",
54
+ "test:run": "vp test run"
55
+ },
56
+ "main": "./dist/index.js",
57
+ "module": "./dist/index.js",
58
+ "types": "./dist/index.d.ts"
59
+ }