new changes
This commit is contained in:
		
							parent
							
								
									31cac3b5b7
								
							
						
					
					
						commit
						31730cad81
					
				| @ -26,7 +26,7 @@ export const SaveButton = ({ | |||||||
|         }, 1000) |         }, 1000) | ||||||
|       }} |       }} | ||||||
|       disabled={disabled} |       disabled={disabled} | ||||||
|       className={`bg-pink-500 text-r mt-4 hover:bg-pink-600 text-white px-4 py-2 rounded-md dark:bg-pink-600 dark:hover:bg-pink-700 ${className}`}> |       className={`inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm dark:bg-white dark:text-gray-800 disabled:opacity-50 ${className}`}> | ||||||
|       {clickedSave ? textOnSave : text} |       {clickedSave ? textOnSave : text} | ||||||
|     </button> |     </button> | ||||||
|   ) |   ) | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ export default function OptionLayout({ | |||||||
|     queryFn: fetchModels |     queryFn: fetchModels | ||||||
|   }) |   }) | ||||||
| 
 | 
 | ||||||
|   const { selectedModel, setSelectedModel } = useMessageOption() |   const { selectedModel, setSelectedModel, clearChat } = useMessageOption() | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <Layout className="bg-white dark:bg-[#171717] md:flex"> |     <Layout className="bg-white dark:bg-[#171717] md:flex"> | ||||||
| @ -79,19 +79,29 @@ export default function OptionLayout({ | |||||||
|             </button> |             </button> | ||||||
|           </div> |           </div> | ||||||
|           <div> |           <div> | ||||||
|             <button className="inline-flex items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 "> |             <button | ||||||
|  |               onClick={clearChat} | ||||||
|  |               className="inline-flex items-center rounded-lg border  dark:border-gray-700 bg-transparent px-3 py-3 text-sm font-medium leading-4 text-gray-800 shadow-sm  dark:text-white disabled:opacity-50 "> | ||||||
|               <SquarePen className="h-4 w-4 mr-3" /> |               <SquarePen className="h-4 w-4 mr-3" /> | ||||||
|               New Chat |               New Chat | ||||||
|             </button> |             </button> | ||||||
|           </div> |           </div> | ||||||
|  |           <span className="text-lg font-thin text-zinc-300 dark:text-zinc-600"> | ||||||
|  |             {"/"} | ||||||
|  |           </span> | ||||||
|           <div> |           <div> | ||||||
|             <Select |             <Select | ||||||
|               value={selectedModel} |               value={selectedModel} | ||||||
|               onChange={setSelectedModel} |               onChange={setSelectedModel} | ||||||
|               size="large" |               size="large" | ||||||
|               loading={isModelsLoading || isModelsFetching} |               loading={isModelsLoading || isModelsFetching} | ||||||
|  |               filterOption={(input, option) => | ||||||
|  |                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || | ||||||
|  |                 option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | ||||||
|  |               } | ||||||
|  |               showSearch | ||||||
|               placeholder="Select a model" |               placeholder="Select a model" | ||||||
|               className="w-64" |               className="w-64 " | ||||||
|               options={models?.map((model) => ({ |               options={models?.map((model) => ({ | ||||||
|                 label: model.name, |                 label: model.name, | ||||||
|                 value: model.model |                 value: model.model | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| import { useQuery } from "@tanstack/react-query" | import { useQuery } from "@tanstack/react-query" | ||||||
|  | import { RotateCcw } from "lucide-react" | ||||||
| import { useEffect, useState } from "react" | import { useEffect, useState } from "react" | ||||||
| import { useMessage } from "~hooks/useMessage" |  | ||||||
| import { useMessageOption } from "~hooks/useMessageOption" |  | ||||||
| import { | import { | ||||||
|   getOllamaURL, |   getOllamaURL, | ||||||
|   isOllamaRunning, |   isOllamaRunning, | ||||||
| @ -74,7 +73,8 @@ export const PlaygroundEmpty = () => { | |||||||
|                   saveOllamaURL(ollamaURL) |                   saveOllamaURL(ollamaURL) | ||||||
|                   refetch() |                   refetch() | ||||||
|                 }} |                 }} | ||||||
|                 className="bg-pink-500 mt-4 hover:bg-pink-600 text-white px-4 py-2 rounded-md dark:bg-pink-600 dark:hover:bg-pink-700"> |                 className="inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 "> | ||||||
|  |                 <RotateCcw className="h-4 w-4 mr-3" /> | ||||||
|                 Retry |                 Retry | ||||||
|               </button> |               </button> | ||||||
|             </div> |             </div> | ||||||
|  | |||||||
| @ -7,8 +7,9 @@ import XMarkIcon from "@heroicons/react/24/outline/XMarkIcon" | |||||||
| import { toBase64 } from "~libs/to-base64" | import { toBase64 } from "~libs/to-base64" | ||||||
| import { useMessageOption } from "~hooks/useMessageOption" | import { useMessageOption } from "~hooks/useMessageOption" | ||||||
| import { Tooltip } from "antd" | import { Tooltip } from "antd" | ||||||
| import { MicIcon } from "lucide-react" | import { MicIcon, MicOffIcon } from "lucide-react" | ||||||
| import { Image } from "antd" | import { Image } from "antd" | ||||||
|  | import { useSpeechRecognition } from "~hooks/useSpeechRecognition" | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|   dropedFile: File | undefined |   dropedFile: File | undefined | ||||||
| @ -59,7 +60,16 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | |||||||
| 
 | 
 | ||||||
|   useDynamicTextareaSize(textareaRef, form.values.message, 300) |   useDynamicTextareaSize(textareaRef, form.values.message, 300) | ||||||
| 
 | 
 | ||||||
|   const { onSubmit, selectedModel, chatMode, clearChat } = useMessageOption() |   const { onSubmit, selectedModel, chatMode, speechToTextLanguage } = | ||||||
|  |     useMessageOption() | ||||||
|  | 
 | ||||||
|  |   const { isListening, start, stop, transcript } = useSpeechRecognition() | ||||||
|  | 
 | ||||||
|  |   React.useEffect(() => { | ||||||
|  |     if (isListening) { | ||||||
|  |       form.setFieldValue("message", transcript) | ||||||
|  |     } | ||||||
|  |   }, [transcript]) | ||||||
| 
 | 
 | ||||||
|   const queryClient = useQueryClient() |   const queryClient = useQueryClient() | ||||||
| 
 | 
 | ||||||
| @ -165,8 +175,25 @@ export const PlaygroundForm = ({ dropedFile }: Props) => { | |||||||
|                 <Tooltip title="Voice Message"> |                 <Tooltip title="Voice Message"> | ||||||
|                   <button |                   <button | ||||||
|                     type="button" |                     type="button" | ||||||
|  |                     onClick={() => { | ||||||
|  |                       if (isListening) { | ||||||
|  |                         stop() | ||||||
|  |                       } else { | ||||||
|  |                         start({ | ||||||
|  |                           lang: speechToTextLanguage, | ||||||
|  |                           continuous: true | ||||||
|  |                         }) | ||||||
|  |                       } | ||||||
|  |                     }} | ||||||
|                     className={`flex items-center justify-center dark:text-gray-300`}> |                     className={`flex items-center justify-center dark:text-gray-300`}> | ||||||
|                     <MicIcon className="h-5 w-5" /> |                     {!isListening ? ( | ||||||
|  |                       <MicIcon className="h-5 w-5" /> | ||||||
|  |                     ) : ( | ||||||
|  |                       <div className="relative"> | ||||||
|  |                         <span className="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75"></span> | ||||||
|  |                         <MicIcon className="h-5 w-5" /> | ||||||
|  |                       </div> | ||||||
|  |                     )} | ||||||
|                   </button> |                   </button> | ||||||
|                 </Tooltip> |                 </Tooltip> | ||||||
|                 <Tooltip title="Upload Image"> |                 <Tooltip title="Upload Image"> | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| import { CheckIcon, ClipboardIcon } from "@heroicons/react/24/outline" | import { CheckIcon, ClipboardIcon } from "@heroicons/react/24/outline" | ||||||
| import Markdown from "../../Common/Markdown" | import Markdown from "../../Common/Markdown" | ||||||
| import React from "react" | import React from "react" | ||||||
|  | import { Image } from "antd" | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|   message: string |   message: string | ||||||
| @ -58,39 +59,34 @@ export const PlaygroundMessage = (props: Props) => { | |||||||
|                 {props.images |                 {props.images | ||||||
|                   .filter((image) => image.length > 0) |                   .filter((image) => image.length > 0) | ||||||
|                   .map((image, index) => ( |                   .map((image, index) => ( | ||||||
|                     <div |                     <Image | ||||||
|                       key={index} |                       src={image} | ||||||
|                       className="h-full rounded-md shadow relative"> |                       alt="Uploaded Image" | ||||||
|                       <img |                       width={180} | ||||||
|                         src={image} |                       className="rounded-md relative" | ||||||
|                         alt="Uploaded" |                     /> | ||||||
|                         className="h-full w-auto object-cover rounded-md min-w-[50px]" |  | ||||||
|                       /> |  | ||||||
|                     </div> |  | ||||||
|                   ))} |                   ))} | ||||||
|               </div> |               </div> | ||||||
|             )} |             )} | ||||||
| 
 |  | ||||||
|             |  | ||||||
|           </div> |           </div> | ||||||
|           {props.isBot && ( |           {props.isBot && ( | ||||||
|               <div className="flex space-x-2"> |             <div className="flex space-x-2"> | ||||||
|                 {!props.hideCopy && ( |               {!props.hideCopy && ( | ||||||
|                   <button |                 <button | ||||||
|                     onClick={() => { |                   onClick={() => { | ||||||
|                       navigator.clipboard.writeText(props.message) |                     navigator.clipboard.writeText(props.message) | ||||||
|                       setIsBtnPressed(true) |                     setIsBtnPressed(true) | ||||||
|                     }} |                   }} | ||||||
|                     className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> |                   className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> | ||||||
|                     {!isBtnPressed ? ( |                   {!isBtnPressed ? ( | ||||||
|                       <ClipboardIcon className="w-3 h-3 text-gray-400 group-hover:text-gray-500" /> |                     <ClipboardIcon className="w-3 h-3 text-gray-400 group-hover:text-gray-500" /> | ||||||
|                     ) : ( |                   ) : ( | ||||||
|                       <CheckIcon className="w-3 h-3 text-green-400 group-hover:text-green-500" /> |                     <CheckIcon className="w-3 h-3 text-green-400 group-hover:text-green-500" /> | ||||||
|                     )} |                   )} | ||||||
|                   </button> |                 </button> | ||||||
|                 )} |               )} | ||||||
|               </div> |             </div> | ||||||
|             )} |           )} | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ type Props = { | |||||||
| 
 | 
 | ||||||
| export const Settings = ({ setClose }: Props) => { | export const Settings = ({ setClose }: Props) => { | ||||||
|   return ( |   return ( | ||||||
|     <div className="my-6"> |     <div className="my-6 max-h-[80vh] overflow-y-auto"> | ||||||
|       <Tabs |       <Tabs | ||||||
|         tabPosition="left" |         tabPosition="left" | ||||||
|         defaultActiveKey="1" |         defaultActiveKey="1" | ||||||
| @ -23,14 +23,14 @@ export const Settings = ({ setClose }: Props) => { | |||||||
|           { |           { | ||||||
|             id: "2", |             id: "2", | ||||||
|             key: "2", |             key: "2", | ||||||
|             label: "Ollama", |             label: "Web UI Settings", | ||||||
|             children: <SettingsOllama /> |             children: <SettingOther /> | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|             id: "3", |             id: "3", | ||||||
|             key: "3", |             key: "3", | ||||||
|             label: "Other", |             label: "Ollama Settings", | ||||||
|             children: <SettingOther /> |             children: <SettingsOllama /> | ||||||
|           } |           } | ||||||
|         ]} |         ]} | ||||||
|       /> |       /> | ||||||
|  | |||||||
| @ -2,9 +2,13 @@ import { useQueryClient } from "@tanstack/react-query" | |||||||
| import { useDarkMode } from "~hooks/useDarkmode" | import { useDarkMode } from "~hooks/useDarkmode" | ||||||
| import { useMessageOption } from "~hooks/useMessageOption" | import { useMessageOption } from "~hooks/useMessageOption" | ||||||
| import { PageAssitDatabase } from "~libs/db" | import { PageAssitDatabase } from "~libs/db" | ||||||
|  | import { Select } from "antd" | ||||||
|  | import { Sun, Moon } from "lucide-react" | ||||||
|  | import { SUPPORTED_LANGUAGES } from "~utils/supporetd-languages" | ||||||
| 
 | 
 | ||||||
| export const SettingOther = () => { | export const SettingOther = () => { | ||||||
|   const { clearChat } = useMessageOption() |   const { clearChat, speechToTextLanguage, setSpeechToTextLanguage } = | ||||||
|  |     useMessageOption() | ||||||
| 
 | 
 | ||||||
|   const queryClient = useQueryClient() |   const queryClient = useQueryClient() | ||||||
| 
 | 
 | ||||||
| @ -13,18 +17,43 @@ export const SettingOther = () => { | |||||||
|   return ( |   return ( | ||||||
|     <div className="flex flex-col space-y-4"> |     <div className="flex flex-col space-y-4"> | ||||||
|       <div className="flex flex-row justify-between"> |       <div className="flex flex-row justify-between"> | ||||||
|         <span className="text-gray-500 dark:text-gray-400 text-lg"> |         <span className="text-gray-500 dark:text-gray-400 text-md"> | ||||||
|  |           Speech Recognition Language | ||||||
|  |         </span> | ||||||
|  | 
 | ||||||
|  |         <Select | ||||||
|  |           placeholder="Select Language" | ||||||
|  |           allowClear | ||||||
|  |           showSearch | ||||||
|  |           options={SUPPORTED_LANGUAGES} | ||||||
|  |           value={speechToTextLanguage} | ||||||
|  |           filterOption={(input, option) => | ||||||
|  |                 option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || | ||||||
|  |                 option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | ||||||
|  |               } | ||||||
|  |           onChange={(value) => { | ||||||
|  |             setSpeechToTextLanguage(value) | ||||||
|  |           }} | ||||||
|  |         /> | ||||||
|  |       </div> | ||||||
|  |       <div className="flex flex-row justify-between"> | ||||||
|  |         <span className="text-gray-500 dark:text-gray-400 text-md"> | ||||||
|           Change Theme |           Change Theme | ||||||
|         </span> |         </span> | ||||||
| 
 | 
 | ||||||
|         <button |         <button | ||||||
|           onClick={toggleDarkMode} |           onClick={toggleDarkMode} | ||||||
|           className="bg-blue-500 dark:bg-blue-600 text-white dark:text-gray-200 px-4 py-2 rounded-md"> |           className={`inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm  dark:bg-white dark:text-gray-800 disabled:opacity-50 `}> | ||||||
|  |           {mode === "dark" ? ( | ||||||
|  |             <Sun className="w-4 h-4 mr-2" /> | ||||||
|  |           ) : ( | ||||||
|  |             <Moon className="w-4 h-4 mr-2" /> | ||||||
|  |           )} | ||||||
|           {mode === "dark" ? "Light" : "Dark"} |           {mode === "dark" ? "Light" : "Dark"} | ||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
|       <div className="flex flex-row justify-between"> |       <div className="flex flex-row justify-between"> | ||||||
|         <span className="text-gray-500 dark:text-gray-400 text-lg"> |         <span className="text-gray-500 dark:text-gray-400 text-md"> | ||||||
|           Delete Chat History |           Delete Chat History | ||||||
|         </span> |         </span> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -10,3 +10,8 @@ | |||||||
| @tailwind base; | @tailwind base; | ||||||
| @tailwind components; | @tailwind components; | ||||||
| @tailwind utilities; | @tailwind utilities; | ||||||
|  | 
 | ||||||
|  | .ant-select-selection-search-input { | ||||||
|  |   border: none !important; | ||||||
|  |   box-shadow: none !important; | ||||||
|  | } | ||||||
| @ -89,7 +89,9 @@ export const useMessageOption = () => { | |||||||
|     selectedModel, |     selectedModel, | ||||||
|     setSelectedModel, |     setSelectedModel, | ||||||
|     chatMode, |     chatMode, | ||||||
|     setChatMode |     setChatMode, | ||||||
|  |     speechToTextLanguage, | ||||||
|  |     setSpeechToTextLanguage | ||||||
|   } = useStoreMessageOption() |   } = useStoreMessageOption() | ||||||
| 
 | 
 | ||||||
|   const abortControllerRef = React.useRef<AbortController | null>(null) |   const abortControllerRef = React.useRef<AbortController | null>(null) | ||||||
| @ -301,6 +303,8 @@ export const useMessageOption = () => { | |||||||
|     selectedModel, |     selectedModel, | ||||||
|     setSelectedModel, |     setSelectedModel, | ||||||
|     chatMode, |     chatMode, | ||||||
|     setChatMode |     setChatMode, | ||||||
|  |     speechToTextLanguage, | ||||||
|  |     setSpeechToTextLanguage | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										176
									
								
								src/hooks/useSpeechRecognition.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/hooks/useSpeechRecognition.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | |||||||
|  | import { useRef, useEffect, useState, useCallback } from "react"; | ||||||
|  | 
 | ||||||
|  | type SpeechRecognitionEvent = { | ||||||
|  |   results: SpeechRecognitionResultList; | ||||||
|  |   resultIndex: number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | declare global { | ||||||
|  |   interface SpeechRecognitionErrorEvent extends Event { | ||||||
|  |     error: string; | ||||||
|  |   } | ||||||
|  |   interface Window { | ||||||
|  |     SpeechRecognition: any; | ||||||
|  |     webkitSpeechRecognition: any; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | type SpeechRecognition = { | ||||||
|  |   lang: string; | ||||||
|  |   interimResults: boolean; | ||||||
|  |   continuous: boolean; | ||||||
|  |   maxAlternatives: number; | ||||||
|  |   grammars: any; | ||||||
|  |   onresult: (event: SpeechRecognitionEvent) => void; | ||||||
|  |   onerror: (event: Event) => void; | ||||||
|  |   onend: () => void; | ||||||
|  |   start: () => void; | ||||||
|  |   stop: () => void; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type SpeechRecognitionProps = { | ||||||
|  |   onEnd?: () => void; | ||||||
|  |   onResult?: (transcript: string) => void; | ||||||
|  |   onError?: (event: Event) => void; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type ListenArgs = { | ||||||
|  |   lang?: string; | ||||||
|  |   interimResults?: boolean; | ||||||
|  |   continuous?: boolean; | ||||||
|  |   maxAlternatives?: number; | ||||||
|  |   grammars?: any; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type SpeechRecognitionHook = { | ||||||
|  |   start: (args?: ListenArgs) => void; | ||||||
|  |   isListening: boolean; | ||||||
|  |   stop: () => void; | ||||||
|  |   supported: boolean; | ||||||
|  |   transcript: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const useEventCallback = <T extends (...args: any[]) => any>( | ||||||
|  |   fn: T, | ||||||
|  |   dependencies: any[] | ||||||
|  | ) => { | ||||||
|  |   const ref = useRef<T>(); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     ref.current = fn; | ||||||
|  |   }, [fn, ...dependencies]); | ||||||
|  | 
 | ||||||
|  |   return useCallback( | ||||||
|  |     (...args: Parameters<T>) => { | ||||||
|  |       const fn = ref.current; | ||||||
|  |       return fn!(...args); | ||||||
|  |     }, | ||||||
|  |     [ref] | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const useSpeechRecognition = ( | ||||||
|  |   props: SpeechRecognitionProps = {} | ||||||
|  | ): SpeechRecognitionHook => { | ||||||
|  |   const { onEnd = () => {}, onResult = () => {}, onError = () => {} } = props; | ||||||
|  |   const recognition = useRef<SpeechRecognition | null>(null); | ||||||
|  |   const [listening, setListening] = useState<boolean>(false); | ||||||
|  |   const [supported, setSupported] = useState<boolean>(false); | ||||||
|  |   const [liveTranscript, setLiveTranscript] = useState<string>(""); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (typeof window === "undefined") return; | ||||||
|  |     window.SpeechRecognition = | ||||||
|  |       window.SpeechRecognition || window.webkitSpeechRecognition; | ||||||
|  |     console.log("window.SpeechRecognition", window.SpeechRecognition); | ||||||
|  |     if (window.SpeechRecognition) { | ||||||
|  |       setSupported(true); | ||||||
|  |       recognition.current = new window.SpeechRecognition(); | ||||||
|  |     } | ||||||
|  |   }, []); | ||||||
|  | 
 | ||||||
|  |   const processResult = (event: SpeechRecognitionEvent) => { | ||||||
|  |     const transcript = Array.from(event.results) | ||||||
|  |       .map((result) => result[0]) | ||||||
|  |       .map((result) => result.transcript) | ||||||
|  |       .join(""); | ||||||
|  | 
 | ||||||
|  |     onResult(transcript); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const handleError = (event: Event) => { | ||||||
|  |     if ((event as SpeechRecognitionErrorEvent).error === "not-allowed") { | ||||||
|  |       if (recognition.current) { | ||||||
|  |         recognition.current.onend = null; | ||||||
|  |       } | ||||||
|  |       setListening(false); | ||||||
|  |     } | ||||||
|  |     onError(event); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const listen = useEventCallback( | ||||||
|  |     (args: ListenArgs = {}) => { | ||||||
|  |       if (listening || !supported) return; | ||||||
|  |       const { | ||||||
|  |         lang = "", | ||||||
|  |         interimResults = true, | ||||||
|  |         continuous = false, | ||||||
|  |         maxAlternatives = 1, | ||||||
|  |         grammars, | ||||||
|  |       } = args; | ||||||
|  |       setListening(true); | ||||||
|  |       setLiveTranscript(""); | ||||||
|  |       if (recognition.current) { | ||||||
|  |         recognition.current.lang = lang; | ||||||
|  |         recognition.current.interimResults = interimResults; | ||||||
|  |         recognition.current.onresult = (event) => { | ||||||
|  |           processResult(event); | ||||||
|  |           const transcript = Array.from(event.results) | ||||||
|  |             .map((result) => result[0]) | ||||||
|  |             .map((result) => result.transcript) | ||||||
|  |             .join(""); | ||||||
|  |           setLiveTranscript(transcript); | ||||||
|  |         }; | ||||||
|  |         recognition.current.onerror = handleError; | ||||||
|  |         recognition.current.continuous = continuous; | ||||||
|  |         recognition.current.maxAlternatives = maxAlternatives; | ||||||
|  | 
 | ||||||
|  |         if (grammars) { | ||||||
|  |           recognition.current.grammars = grammars; | ||||||
|  |         } | ||||||
|  |         recognition.current.onend = () => { | ||||||
|  |           if (recognition.current) { | ||||||
|  |             recognition.current.start(); | ||||||
|  |           } | ||||||
|  |         }; | ||||||
|  |         if (recognition.current) { | ||||||
|  |           recognition.current.start(); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     [listening, supported, recognition] | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const stop = useEventCallback(() => { | ||||||
|  |     if (!listening || !supported) return; | ||||||
|  |     if (recognition.current) { | ||||||
|  |       recognition.current.onresult = null; | ||||||
|  |       recognition.current.onend = null; | ||||||
|  |       recognition.current.onerror = null; | ||||||
|  |       setListening(false); | ||||||
|  |       recognition.current.stop(); | ||||||
|  |     } | ||||||
|  |     onEnd(); | ||||||
|  |   }, [listening, supported, recognition, onEnd]); | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     start: listen, | ||||||
|  |     isListening: listening, | ||||||
|  |     stop, | ||||||
|  |     supported, | ||||||
|  |     transcript: liveTranscript, | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| @ -10,7 +10,7 @@ export type Message = { | |||||||
| 
 | 
 | ||||||
| export type ChatHistory = { | export type ChatHistory = { | ||||||
|   role: "user" | "assistant" | "system" |   role: "user" | "assistant" | "system" | ||||||
|   content: string, |   content: string | ||||||
|   image?: string |   image?: string | ||||||
| }[] | }[] | ||||||
| 
 | 
 | ||||||
| @ -35,6 +35,8 @@ type State = { | |||||||
|   setChatMode: (chatMode: "normal" | "rag") => void |   setChatMode: (chatMode: "normal" | "rag") => void | ||||||
|   isEmbedding: boolean |   isEmbedding: boolean | ||||||
|   setIsEmbedding: (isEmbedding: boolean) => void |   setIsEmbedding: (isEmbedding: boolean) => void | ||||||
|  |   speechToTextLanguage: string | ||||||
|  |   setSpeechToTextLanguage: (language: string) => void | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const useStoreMessageOption = create<State>((set) => ({ | export const useStoreMessageOption = create<State>((set) => ({ | ||||||
| @ -52,11 +54,13 @@ export const useStoreMessageOption = create<State>((set) => ({ | |||||||
|   setIsLoading: (isLoading) => set({ isLoading }), |   setIsLoading: (isLoading) => set({ isLoading }), | ||||||
|   isProcessing: false, |   isProcessing: false, | ||||||
|   setIsProcessing: (isProcessing) => set({ isProcessing }), |   setIsProcessing: (isProcessing) => set({ isProcessing }), | ||||||
|   defaultSpeechToTextLanguage: "en-US", |   speechToTextLanguage: "en-US", | ||||||
|  |   setSpeechToTextLanguage: (language) => | ||||||
|  |     set({ speechToTextLanguage: language }), | ||||||
|   selectedModel: null, |   selectedModel: null, | ||||||
|   setSelectedModel: (selectedModel) => set({ selectedModel }), |   setSelectedModel: (selectedModel) => set({ selectedModel }), | ||||||
|   chatMode: "normal", |   chatMode: "normal", | ||||||
|   setChatMode: (chatMode) => set({ chatMode }), |   setChatMode: (chatMode) => set({ chatMode }), | ||||||
|   isEmbedding: false, |   isEmbedding: false, | ||||||
|   setIsEmbedding: (isEmbedding) => set({ isEmbedding }), |   setIsEmbedding: (isEmbedding) => set({ isEmbedding }) | ||||||
| })) | })) | ||||||
|  | |||||||
							
								
								
									
										406
									
								
								src/utils/supporetd-languages.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										406
									
								
								src/utils/supporetd-languages.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,406 @@ | |||||||
|  | export const SUPPORTED_LANGUAGES = [ | ||||||
|  |   { | ||||||
|  |     label: "Afrikaans ", | ||||||
|  |     value: "af-ZA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "አማርኛ", | ||||||
|  |     value: "am-ET" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Azərbaycanca", | ||||||
|  |     value: "az-AZ" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "বাংলা", | ||||||
|  |     value: "bn-BD" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "বাংলাদেশ", | ||||||
|  |     value: "bn-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Bahasa Indonesia", | ||||||
|  |     value: "id-ID" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Bahasa Melayu", | ||||||
|  |     value: "ms-MY" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Català", | ||||||
|  |     value: "ca-ES" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Čeština", | ||||||
|  |     value: "cs-CZ" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Dansk", | ||||||
|  |     value: "da-DK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Deutsch", | ||||||
|  |     value: "de-DE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Australia)", | ||||||
|  |     value: "en-AU" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Canada)", | ||||||
|  |     value: "en-CA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (India)", | ||||||
|  |     value: "en-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Kenya)", | ||||||
|  |     value: "en-KE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Tanzania)", | ||||||
|  |     value: "en-TZ" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Ghana)", | ||||||
|  |     value: "en-GH" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (New Zealand)", | ||||||
|  |     value: "en-NZ" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Nigeria)", | ||||||
|  |     value: "en-NG" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (South Africa)", | ||||||
|  |     value: "en-ZA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (Philippines)", | ||||||
|  |     value: "en-PH" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (United Kingdom)", | ||||||
|  |     value: "en-GB" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "English (United States)", | ||||||
|  |     value: "en-US" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Argentina)", | ||||||
|  |     value: "es-AR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Bolivia)", | ||||||
|  |     value: "es-BO" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Chile)", | ||||||
|  |     value: "es-CL" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Colombia)", | ||||||
|  |     value: "es-CO" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Costa Rica)", | ||||||
|  |     value: "es-CR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Ecuador)", | ||||||
|  |     value: "es-EC" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (El Salvador)", | ||||||
|  |     value: "es-SV" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (España)", | ||||||
|  |     value: "es-ES" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Estados Unidos)", | ||||||
|  |     value: "es-US" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Guatemala)", | ||||||
|  |     value: "es-GT" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Honduras)", | ||||||
|  |     value: "es-HN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (México)", | ||||||
|  |     value: "es-MX" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Nicaragua)", | ||||||
|  |     value: "es-NI" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Panamá)", | ||||||
|  |     value: "es-PA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Paraguay)", | ||||||
|  |     value: "es-PY" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Perú)", | ||||||
|  |     value: "es-PE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Puerto Rico)", | ||||||
|  |     value: "es-PR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (República Dominicana)", | ||||||
|  |     value: "es-DO" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Uruguay)", | ||||||
|  |     value: "es-UY" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Español (Venezuela)", | ||||||
|  |     value: "es-VE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Euskara", | ||||||
|  |     value: "eu-ES" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Filipino", | ||||||
|  |     value: "fil-PH" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Français", | ||||||
|  |     value: "fr-FR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Basa Jawa", | ||||||
|  |     value: "jv-ID" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Galego", | ||||||
|  |     value: "gl-ES" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "ગુજરાતી", | ||||||
|  |     value: "gu-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Hrvatski", | ||||||
|  |     value: "hr-HR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "IsiZulu", | ||||||
|  |     value: "zu-ZA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Íslenska", | ||||||
|  |     value: "is-IS" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Italiano (Italia)", | ||||||
|  |     value: "it-IT" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Italiano (Svizzera)", | ||||||
|  |     value: "it-CH" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "ಕನ್ನಡ", | ||||||
|  |     value: "kn-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "ភាសាខ្មែរ", | ||||||
|  |     value: "km-KH" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Latviešu)", | ||||||
|  |     value: "lv-LV" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Lietuvių", | ||||||
|  |     value: "lt-LT" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "മലയാളം", | ||||||
|  |     value: "ml-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "मराठी ", | ||||||
|  |     value: "mr-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Magyar", | ||||||
|  |     value: "hu-HU" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "ລາວ", | ||||||
|  |     value: "lo-LA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Nederlands", | ||||||
|  |     value: "nl-NL" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "नेपाली भाषा", | ||||||
|  |     value: "ne-NP" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Norsk bokmål", | ||||||
|  |     value: "nb-NO" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Polski", | ||||||
|  |     value: "pl-PL" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Português (Brasil)", | ||||||
|  |     value: "pt-BR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Português (Portugal)", | ||||||
|  |     value: "pt-PT" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Română", | ||||||
|  |     value: "ro-RO" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "සිංහල", | ||||||
|  |     value: "si-LK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Slovenščina", | ||||||
|  |     value: "sl-SI" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Basa Sunda", | ||||||
|  |     value: "su-ID" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Slovenčina ", | ||||||
|  |     value: "sk-SK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Suomi", | ||||||
|  |     value: "fi-FI" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Svenska", | ||||||
|  |     value: "sv-SE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Kiswahili (Tanzania)", | ||||||
|  |     value: "sw-TZ" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Kiswahili (Kenya)", | ||||||
|  |     value: "sw-KE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "ქართული ", | ||||||
|  |     value: "ka-GE" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Հայերեն", | ||||||
|  |     value: "hy-AM" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "தமிழ் (இந்தியா)", | ||||||
|  |     value: "ta-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "தமிழ் (சிங்கப்பூர்)", | ||||||
|  |     value: "ta-SG" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "தமிழ் (இலங்கை)", | ||||||
|  |     value: "ta-LK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "தமிழ் (மலேசியா)", | ||||||
|  |     value: "ta-MY" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "తెలుగు", | ||||||
|  |     value: "te-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Tiếng Việt", | ||||||
|  |     value: "vi-VN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Türkçe", | ||||||
|  |     value: "tr-TR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "اُردُو (پاکستان)", | ||||||
|  |     value: "ur-PK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "اُردُو (بھارت)", | ||||||
|  |     value: "ur-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Ελληνικά ", | ||||||
|  |     value: "el-GR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "български", | ||||||
|  |     value: "bg-BG" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Русский", | ||||||
|  |     value: "ru-RU" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Српски", | ||||||
|  |     value: "sr-RS" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "Українська", | ||||||
|  |     value: "uk-UA" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "한국어 ", | ||||||
|  |     value: "ko-KR" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "中文 (普通话 中国大陆)", | ||||||
|  |     value: "cmn-Hans-CN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "中文 (普通话 香港)", | ||||||
|  |     value: "cmn-Hans-HK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "中文 (台灣)", | ||||||
|  |     value: "cmn-Hant-TW" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "粵語 (香港)", | ||||||
|  |     value: "yue-Hant-HK" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "日本語", | ||||||
|  |     value: "ja-JP" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "हिन्दी ", | ||||||
|  |     value: "hi-IN" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: "ภาษาไทย", | ||||||
|  |     value: "th-TH" | ||||||
|  |   } | ||||||
|  | ] | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user