react-text-to-speech
Advanced tools
Comparing version
@@ -9,3 +9,3 @@ import React from "react"; | ||
}; | ||
export declare function useSpeech({ text, pitch, rate, volume, lang, voiceURI, highlightText, highlightProps, preserveUtteranceQueue, maxChunkSize, onError, onStart, onResume, onPause, onStop, onBoundary, onQueueChange, }: useSpeechProps): { | ||
export declare function useSpeech({ text, pitch, rate, volume, lang, voiceURI, autoPlay, preserveUtteranceQueue, highlightText, highlightProps, maxChunkSize, onError, onStart, onResume, onPause, onStop, onBoundary, onQueueChange, }: useSpeechProps): { | ||
Text: () => React.ReactNode; | ||
@@ -12,0 +12,0 @@ speechStatus: SpeechStatus; |
import React, { cloneElement, isValidElement, useEffect, useMemo, useRef, useState } from "react"; | ||
import { specialSymbol } from "./constants.js"; | ||
import { addToQueue, clearQueue, clearQueueHook, clearQueueUnload, dequeue, removeFromQueue, speakFromQueue, subscribe } from "./queue.js"; | ||
import { state } from "./state.js"; | ||
import { ArrayToText, cancel, findCharIndex, getIndex, isMobile, isParent, JSXToArray, sanitize, TextToChunks } from "./utils.js"; | ||
@@ -9,3 +11,3 @@ export function useQueue() { | ||
} | ||
export function useSpeech({ text, pitch = 1, rate = 1, volume = 1, lang, voiceURI, highlightText = false, highlightProps, preserveUtteranceQueue = false, maxChunkSize, onError = console.error, onStart, onResume, onPause, onStop, onBoundary, onQueueChange, }) { | ||
export function useSpeech({ text, pitch = 1, rate = 1, volume = 1, lang, voiceURI, autoPlay = false, preserveUtteranceQueue = false, highlightText = false, highlightProps, maxChunkSize, onError = console.error, onStart, onResume, onPause, onStop, onBoundary, onQueueChange, }) { | ||
const [speechStatus, speechStatusRef, setSpeechStatus] = useStateRef("stopped"); | ||
@@ -29,4 +31,6 @@ const [speakingWord, setSpeakingWord] = useState(); | ||
const numChunks = chunks.length; | ||
let currentChunk = 0, offset = 0; | ||
const utterance = new SpeechSynthesisUtterance(chunks[currentChunk]); | ||
let currentChunk = 0; | ||
let currentText = chunks[currentChunk] || ""; | ||
const utterance = new SpeechSynthesisUtterance(currentText.trimStart()); | ||
let offset = currentText.length - utterance.text.length; | ||
utterance.pitch = pitch; | ||
@@ -50,5 +54,7 @@ utterance.rate = rate; | ||
const stopEventHandler = (event) => { | ||
if (event.type === "end" && currentChunk < numChunks - 1) { | ||
offset += chunks[currentChunk].length; | ||
utterance.text = chunks[++currentChunk]; | ||
if (state.stopReason === "auto" && currentChunk < numChunks - 1) { | ||
offset += utterance.text.length; | ||
currentText = chunks[++currentChunk]; | ||
utterance.text = currentText.trimStart(); | ||
offset += currentText.length - utterance.text.length; | ||
return speakFromQueue(); | ||
@@ -87,4 +93,6 @@ } | ||
utterance.onboundary = (event) => { | ||
const { charIndex, charLength } = event; | ||
if (charLength && utterance.text[charIndex] === "\u200E") { | ||
const { charIndex, charLength, name } = event; | ||
if (name === "sentence") | ||
setSpeakingWord(null); | ||
else if (utterance.text[charIndex] === specialSymbol) { | ||
setSpeakingWord({ index: findCharIndex(words, offset + charIndex - 1), length: 1 }); | ||
@@ -100,2 +108,3 @@ offset -= charLength + 1; | ||
addToQueue({ utterance, setSpeechStatus }, onQueueChange); | ||
setSpeechStatus("started"); | ||
if (synth.speaking) { | ||
@@ -111,3 +120,2 @@ if (preserveUtteranceQueue && speechStatus !== "started") { | ||
speakFromQueue(); | ||
setSpeechStatus("started"); | ||
} | ||
@@ -149,2 +157,4 @@ function pause() { | ||
useEffect(() => { | ||
if (autoPlay) | ||
start(); | ||
return () => stop(speechStatusRef.current); | ||
@@ -151,0 +161,0 @@ }, [stringifiedWords]); |
@@ -0,1 +1,2 @@ | ||
import { setState } from "./state.js"; | ||
import { cancel } from "./utils.js"; | ||
@@ -41,4 +42,6 @@ const queue = []; | ||
const item = queue[0]; | ||
if (item) | ||
window.speechSynthesis.speak(item.utterance); | ||
if (!item) | ||
return; | ||
setState({ stopReason: "auto" }); | ||
window.speechSynthesis.speak(item.utterance); | ||
} | ||
@@ -45,0 +48,0 @@ export function subscribe(callback) { |
@@ -13,5 +13,6 @@ import { DetailedHTMLProps, HTMLAttributes, ReactNode } from "react"; | ||
voiceURI?: string | string[]; | ||
autoPlay?: boolean; | ||
preserveUtteranceQueue?: boolean; | ||
highlightText?: boolean; | ||
highlightProps?: SpanProps; | ||
preserveUtteranceQueue?: boolean; | ||
maxChunkSize?: number; | ||
@@ -55,2 +56,5 @@ onError?: SpeechSynthesisErrorHandler; | ||
export type Index = string | number; | ||
export type State = { | ||
stopReason: "auto" | "manual"; | ||
}; | ||
export type StringArray = string | StringArray[]; |
import { isValidElement } from "react"; | ||
import { desktopChunkSize, minChunkSize, mobileChunkSize, specialSymbol, symbolMapping } from "./constants.js"; | ||
import { setState } from "./state.js"; | ||
export function ArrayToText(element) { | ||
@@ -18,9 +20,10 @@ if (typeof element === "string") | ||
var _a; | ||
if (typeof window !== "undefined") | ||
(_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.cancel(); | ||
if (typeof window === "undefined") | ||
return; | ||
setState({ stopReason: "manual" }); | ||
(_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.cancel(); | ||
} | ||
export function TextToChunks(text, size) { | ||
size = size ? Math.max(size, minChunkSize) : isMobile() ? mobileChunkSize : desktopChunkSize; | ||
const length = text.length; | ||
if (!size) | ||
size = isMobile() ? 250 : 1000; | ||
const result = []; | ||
@@ -78,2 +81,2 @@ let startIndex = 0; | ||
} | ||
export const sanitize = (text) => text.replace(/[<>]|(&[^\s;]+);/g, (match, group) => (match === "<" ? " \u200Eless-than " : match === ">" ? " \u200Egreater-than " : group + ")")); | ||
export const sanitize = (text) => text.replace(/[<>]|(&[^\s;]+);/g, (match, group) => (group ? group + ")" : ` ${specialSymbol}${symbolMapping[match]} `)); |
{ | ||
"name": "react-text-to-speech", | ||
"version": "0.18.0", | ||
"version": "0.19.0", | ||
"description": "An easy-to-use React.js component that leverages the Web Speech API to convert text to speech.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
29833
5.67%19
26.67%518
7.92%