import React, { useState, useRef, useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { listen } from "@tauri-apps/api/event"; import { SettingContainer } from "../ui/SettingContainer"; import { ResetButton } from "../ui/ResetButton"; import { useSettings } from "../../hooks/useSettings"; import { useModels } from "../../hooks/useModels"; import { LANGUAGES } from "../../lib/constants/languages"; interface LanguageSelectorProps { descriptionMode?: "inline" | "tooltip"; grouped?: boolean; } const unsupportedModels = ["parakeet-tdt-0.6b-v2", "parakeet-tdt-0.6b-v3"]; export const LanguageSelector: React.FC = ({ descriptionMode = "tooltip", grouped = false, }) => { const { t } = useTranslation(); const { getSetting, updateSetting, resetSetting, isUpdating } = useSettings(); const { currentModel, loadCurrentModel } = useModels(); const [isOpen, setIsOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const dropdownRef = useRef(null); const searchInputRef = useRef(null); const selectedLanguage = getSetting("selected_language") || "auto"; const isUnsupported = unsupportedModels.includes(currentModel); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( dropdownRef.current && !dropdownRef.current.contains(event.target as Node) ) { setIsOpen(false); setSearchQuery(""); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, []); // Listen for model state changes to update UI reactively useEffect(() => { const modelStateUnlisten = listen("model-state-changed", () => { loadCurrentModel(); }); return () => { modelStateUnlisten.then((fn) => fn()); }; }, [loadCurrentModel]); useEffect(() => { if (isOpen && searchInputRef.current) { searchInputRef.current.focus(); } }, [isOpen]); const filteredLanguages = useMemo( () => LANGUAGES.filter((language) => language.label.toLowerCase().includes(searchQuery.toLowerCase()), ), [searchQuery], ); const selectedLanguageName = isUnsupported ? t("settings.general.language.auto") : LANGUAGES.find((lang) => lang.value === selectedLanguage)?.label || t("settings.general.language.auto"); const handleLanguageSelect = async (languageCode: string) => { await updateSetting("selected_language", languageCode); setIsOpen(false); setSearchQuery(""); }; const handleReset = async () => { await resetSetting("selected_language"); }; const handleToggle = () => { if (isUpdating("selected_language") || isUnsupported) return; setIsOpen(!isOpen); }; const handleSearchChange = (event: React.ChangeEvent) => { setSearchQuery(event.target.value); }; const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === "Enter" && filteredLanguages.length > 0) { // Select first filtered language on Enter handleLanguageSelect(filteredLanguages[0].value); } else if (event.key === "Escape") { setIsOpen(false); setSearchQuery(""); } }; return (
{isOpen && !isUpdating("selected_language") && !isUnsupported && (
{/* Search input */}
{filteredLanguages.length === 0 ? (
{t("settings.general.language.noResults")}
) : ( filteredLanguages.map((language) => ( )) )}
)}
{isUpdating("selected_language") && (
)}
); };