-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Expand file tree
/
Copy pathindex.jsx
More file actions
129 lines (118 loc) · 3.7 KB
/
Copy pathindex.jsx
File metadata and controls
129 lines (118 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { useEffect, useCallback } from "react";
import { Microphone } from "@phosphor-icons/react";
import { Tooltip } from "react-tooltip";
import _regeneratorRuntime from "regenerator-runtime";
import SpeechRecognition, {
useSpeechRecognition,
} from "react-speech-recognition";
import { PROMPT_INPUT_EVENT } from "../../PromptInput";
import { useTranslation } from "react-i18next";
import Appearance from "@/models/appearance";
let timeout;
const SILENCE_INTERVAL = 3_200; // wait in seconds of silence before closing.
/**
* Speech-to-text input component for the chat window.
* @param {Object} props - The component props
* @param {(textToAppend: string, autoSubmit: boolean) => void} props.sendCommand - The function to send the command
* @returns {React.ReactElement} The SpeechToText component
*/
export default function SpeechToText({ sendCommand }) {
const {
transcript,
listening,
resetTranscript,
browserSupportsSpeechRecognition,
browserSupportsContinuousListening,
isMicrophoneAvailable,
} = useSpeechRecognition({
clearTranscriptOnListen: true,
});
const { t } = useTranslation();
function startSTTSession() {
if (!isMicrophoneAvailable) {
alert(
"AnythingLLM does not have access to microphone. Please enable for this site to use this feature."
);
return;
}
resetTranscript();
SpeechRecognition.startListening({
continuous: browserSupportsContinuousListening,
language: window?.navigator?.language ?? "en-US",
});
}
function endSTTSession() {
SpeechRecognition.stopListening();
if (transcript.length > 0) {
sendCommand(transcript, Appearance.get("autoSubmitSttInput"));
}
resetTranscript();
clearTimeout(timeout);
}
const handleKeyPress = useCallback(
(event) => {
if (event.ctrlKey && event.keyCode === 77) {
if (listening) {
endSTTSession();
} else {
startSTTSession();
}
}
},
[listening, endSTTSession, startSTTSession]
);
function handlePromptUpdate(e) {
if (!e?.detail && timeout) {
endSTTSession();
clearTimeout(timeout);
}
}
useEffect(() => {
document.addEventListener("keydown", handleKeyPress);
return () => {
document.removeEventListener("keydown", handleKeyPress);
};
}, [handleKeyPress]);
useEffect(() => {
if (!!window)
window.addEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate);
return () =>
window?.removeEventListener(PROMPT_INPUT_EVENT, handlePromptUpdate);
}, []);
useEffect(() => {
if (transcript?.length > 0 && listening) {
sendCommand(transcript, false);
clearTimeout(timeout);
timeout = setTimeout(() => {
endSTTSession();
}, SILENCE_INTERVAL);
}
}, [transcript, listening]);
if (!browserSupportsSpeechRecognition) return null;
return (
<div
id="text-size-btn"
data-tooltip-id="tooltip-text-size-btn"
data-tooltip-content={t("chat_window.microphone")}
aria-label={t("chat_window.microphone")}
onClick={listening ? endSTTSession : startSTTSession}
className={`border-none relative flex justify-center items-center opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 cursor-pointer ${
!!listening ? "!opacity-100" : ""
}`}
>
<Microphone
weight="fill"
color="var(--theme-sidebar-footer-icon-fill)"
className={`w-[22px] h-[22px] pointer-events-none text-theme-text-primary ${
listening ? "animate-pulse-glow" : ""
}`}
/>
<Tooltip
id="tooltip-text-size-btn"
place="top"
delayShow={300}
className="tooltip !text-xs z-99"
/>
</div>
);
}