mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-11 01:10:11 +01:00
Add browser supported TTS on messages for accessibility (#1388)
This commit is contained in:
parent
a87978d1d9
commit
f0365d41a6
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user