diff --git a/bun.lockb b/bun.lockb
index 52cde83..c793ee5 100644
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index b05f5dd..9832ab4 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"react-i18next": "^14.1.0",
"react-markdown": "8.0.0",
"react-router-dom": "6.10.0",
+ "react-speech-recognition": "^3.10.0",
"react-syntax-highlighter": "^15.5.0",
"react-toastify": "^10.0.4",
"rehype-mathjax": "4.0.3",
@@ -64,6 +65,7 @@
"@types/pubsub-js": "^1.8.6",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
+ "@types/react-speech-recognition": "^3.9.5",
"@types/react-syntax-highlighter": "^15.5.11",
"@types/turndown": "^5.0.4",
"autoprefixer": "^10.4.17",
diff --git a/src/components/Option/Playground/PlaygroundForm.tsx b/src/components/Option/Playground/PlaygroundForm.tsx
index a694149..f4a6502 100644
--- a/src/components/Option/Playground/PlaygroundForm.tsx
+++ b/src/components/Option/Playground/PlaygroundForm.tsx
@@ -6,14 +6,14 @@ import { toBase64 } from "~/libs/to-base64"
import { useMessageOption } from "~/hooks/useMessageOption"
import { Checkbox, Dropdown, Select, Switch, Tooltip } from "antd"
import { Image } from "antd"
-import { useSpeechRecognition } from "~/hooks/useSpeechRecognition"
import { useWebUI } from "~/store/webui"
import { defaultEmbeddingModelForRag } from "~/services/ollama"
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
import { getVariable } from "~/utils/select-varaible"
import { useTranslation } from "react-i18next"
import { KnowledgeSelect } from "../Knowledge/KnowledgeSelect"
-import { SelectedKnowledge } from "../Knowledge/SelectedKnwledge"
+import { useSpeechRecognition } from "react-speech-recognition"
+import SpeechRecognition from "react-speech-recognition"
type Props = {
dropedFile: File | undefined
@@ -84,7 +84,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
useDynamicTextareaSize(textareaRef, form.values.message, 300)
- const { isListening, start, stop, transcript } = useSpeechRecognition()
+ const {
+ transcript,
+ listening: isListening,
+ resetTranscript,
+ browserSupportsSpeechRecognition
+ } = useSpeechRecognition()
+
const { sendWhenEnter, setSendWhenEnter } = useWebUI()
React.useEffect(() => {
@@ -135,6 +141,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
sendWhenEnter
) {
e.preventDefault()
+ stopListening()
form.onSubmit(async (value) => {
if (value.message.trim().length === 0) {
return
@@ -159,6 +166,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
})()
}
}
+
+ const stopListening = async () => {
+ if (isListening) {
+ SpeechRecognition.stopListening()
+ }
+ }
+
return (
{
-
-
-
+ ) : (
+
+
+
+
+ )}
+
+
+ )}
{!selectedKnowledge && (
diff --git a/src/components/Sidepanel/Chat/form.tsx b/src/components/Sidepanel/Chat/form.tsx
index 148ebfd..34ff31e 100644
--- a/src/components/Sidepanel/Chat/form.tsx
+++ b/src/components/Sidepanel/Chat/form.tsx
@@ -5,12 +5,13 @@ import useDynamicTextareaSize from "~/hooks/useDynamicTextareaSize"
import { useMessage } from "~/hooks/useMessage"
import { toBase64 } from "~/libs/to-base64"
import { Checkbox, Dropdown, Image, Tooltip } from "antd"
-import { useSpeechRecognition } from "~/hooks/useSpeechRecognition"
import { useWebUI } from "~/store/webui"
import { defaultEmbeddingModelForRag } from "~/services/ollama"
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
import { useTranslation } from "react-i18next"
import { ModelSelect } from "@/components/Common/ModelSelect"
+import { useSpeechRecognition } from "react-speech-recognition"
+import SpeechRecognition from "react-speech-recognition"
type Props = {
dropedFile: File | undefined
@@ -29,6 +30,20 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
image: ""
}
})
+ const {
+ transcript,
+ listening: isListening,
+ resetTranscript,
+ browserSupportsSpeechRecognition
+ } = useSpeechRecognition({
+
+ })
+
+ const stopListening = async () => {
+ if (isListening) {
+ SpeechRecognition.stopListening()
+ }
+ }
const onInputChange = async (
e: React.ChangeEvent | File
@@ -59,6 +74,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
) {
e.preventDefault()
form.onSubmit(async (value) => {
+ await stopListening()
if (value.message.trim().length === 0) {
return
}
@@ -98,7 +114,6 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
streaming,
setChatMode
} = useMessage()
- const { isListening, start, stop, transcript } = useSpeechRecognition()
React.useEffect(() => {
if (dropedFile) {
@@ -161,6 +176,7 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
return
}
}
+ await stopListening()
form.reset()
textAreaFocus()
await sendMessage({
@@ -196,30 +212,33 @@ export const SidepanelForm = ({ dropedFile }: Props) => {
/>
-
- {
- if (isListening) {
- stop()
- } else {
- start({
- lang: speechToTextLanguage,
- continuous: true
- })
- }
- }}
- className={`flex items-center justify-center dark:text-gray-300`}>
- {!isListening ? (
-
- ) : (
-
-
+ {browserSupportsSpeechRecognition && (
+
+ {
+ if (isListening) {
+ SpeechRecognition.stopListening()
+ } else {
+ resetTranscript()
+ SpeechRecognition.startListening({
+ continuous: true,
+ language: speechToTextLanguage
+ })
+ }
+ }}
+ className={`flex items-center justify-center dark:text-gray-300`}>
+ {!isListening ? (
-
- )}
-
-
+ ) : (
+
+
+
+
+ )}
+
+
+ )}