Add browser supported TTS on messages for accessibility (#1388)

This commit is contained in:
Timothy Carambat 2024-05-13 12:52:47 -07:00 committed by GitHub
parent a87978d1d9
commit f0365d41a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,4 +1,4 @@
import React, { memo, useState } from "react";
import React, { memo, useEffect, useState } from "react";
import useCopyText from "@/hooks/useCopyText";
import {
Check,
@ -6,6 +6,8 @@ import {
ThumbsUp,
ThumbsDown,
ArrowsClockwise,
SpeakerHigh,
PauseCircle,
} from "@phosphor-icons/react";
import { Tooltip } from "react-tooltip";
import Workspace from "@/models/workspace";
@ -28,34 +30,37 @@ const Actions = ({
};
return (
<div className="flex justify-start items-center gap-x-4">
<CopyMessage message={message} />
{isLastMessage &&
!message?.includes("Workspace chat memory was reset!") && (
<RegenerateMessage
regenerateMessage={regenerateMessage}
slug={slug}
chatId={chatId}
/>
<div className="flex w-full justify-between items-center">
<div className="flex justify-start items-center gap-x-4">
<CopyMessage message={message} />
{isLastMessage &&
!message?.includes("Workspace chat memory was reset!") && (
<RegenerateMessage
regenerateMessage={regenerateMessage}
slug={slug}
chatId={chatId}
/>
)}
{chatId && (
<>
<FeedbackButton
isSelected={selectedFeedback === true}
handleFeedback={() => handleFeedback(true)}
tooltipId={`${chatId}-thumbs-up`}
tooltipContent="Good response"
IconComponent={ThumbsUp}
/>
<FeedbackButton
isSelected={selectedFeedback === false}
handleFeedback={() => handleFeedback(false)}
tooltipId={`${chatId}-thumbs-down`}
tooltipContent="Bad response"
IconComponent={ThumbsDown}
/>
</>
)}
{chatId && (
<>
<FeedbackButton
isSelected={selectedFeedback === true}
handleFeedback={() => handleFeedback(true)}
tooltipId={`${chatId}-thumbs-up`}
tooltipContent="Good response"
IconComponent={ThumbsUp}
/>
<FeedbackButton
isSelected={selectedFeedback === false}
handleFeedback={() => handleFeedback(false)}
tooltipId={`${chatId}-thumbs-down`}
tooltipContent="Bad response"
IconComponent={ThumbsDown}
/>
</>
)}
</div>
<TTSMessage message={message} />
</div>
);
};
@ -144,4 +149,62 @@ function RegenerateMessage({ regenerateMessage, chatId }) {
);
}
function TTSMessage({ message }) {
const [speaking, setSpeaking] = useState(false);
const [supported, setSupported] = useState(false);
useEffect(() => {
setSupported("speechSynthesis" in window);
}, []);
function endSpeechUtterance() {
window.speechSynthesis?.cancel();
setSpeaking(false);
return;
}
function speakMessage() {
// if the user is pausing this particular message
// while the synth if speaking we can end it.
// If they are clicking another message's TTS
// we need to ignore that until they pause the one that is playing.
if (window.speechSynthesis.speaking && speaking) {
endSpeechUtterance();
return;
}
if (window.speechSynthesis.speaking && !speaking) return;
const utterance = new SpeechSynthesisUtterance(message);
utterance.addEventListener("end", endSpeechUtterance);
window.speechSynthesis.speak(utterance);
setSpeaking(true);
}
if (!supported) return null;
return (
<div className="mt-3 relative">
<button
onClick={speakMessage}
data-tooltip-id="message-to-speech"
data-tooltip-content={
speaking ? "Pause TTS speech of message" : "TTS Speak message"
}
className="border-none text-zinc-300"
aria-label={speaking ? "Pause speech" : "Speak message"}
>
{speaking ? (
<PauseCircle size={18} className="mb-1" />
) : (
<SpeakerHigh size={18} className="mb-1" />
)}
</button>
<Tooltip
id="message-to-speech"
place="bottom"
delayShow={300}
className="tooltip !text-xs"
/>
</div>
);
}
export default memo(Actions);