Skip to content

Commit

Permalink
Add browser supported TTS on messages for accessibility (#1388)
Browse files Browse the repository at this point in the history
  • Loading branch information
timothycarambat committed May 13, 2024
1 parent a87978d commit f0365d4
Showing 1 changed file with 91 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { memo, useState } from "react";
import React, { memo, useEffect, useState } from "react";
import useCopyText from "@/hooks/useCopyText";
import {
Check,
ClipboardText,
ThumbsUp,
ThumbsDown,
ArrowsClockwise,
SpeakerHigh,
PauseCircle,
} from "@phosphor-icons/react";
import { Tooltip } from "react-tooltip";
import Workspace from "@/models/workspace";
Expand All @@ -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>
);
};
Expand Down Expand Up @@ -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);

0 comments on commit f0365d4

Please sign in to comment.