"use client"; import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent } from "react"; import { Command } from "cmdk"; import { CheckIcon, ChevronDownIcon } from "lucide-react"; import { DICTATION_LANGUAGES, fuzzyScore, type DictationLanguage, } from "@/src/core"; import { MenuShell } from "@/components/menu-shell"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Hint } from "@/components/ui/hint"; import { SettingsRow } from "@/components/settings/controls"; import { useDictationActive } from "@/components/dictation-state"; import { useDictationLanguage } from "@/components/dictation-preference"; type LanguagePreference = ReturnType; export function DictationLanguageSetting() { const preference = useDictationLanguage(); const active = useDictationActive(); return ; } function DictationLanguagePicker({ preference, disabled }: { preference: LanguagePreference; disabled: boolean; }) { const { language, saving, error, load, save } = preference; const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const triggerRef = useRef(null); const label = DICTATION_LANGUAGES.find((item) => item.code === language)?.label ?? "—"; // Own ordering so clearing a search restores the catalog's order. // cmdk handles keyboard/ARIA, but its DOM sorting retains old ranks. const results = query.trim() ? DICTATION_LANGUAGES .map((item) => ({ item, score: fuzzyScore(query, item.code, item.label) })) .filter((item) => item.score !== null) .sort((a, b) => b.score! - a.score!) .map(({ item }) => item) : DICTATION_LANGUAGES; useEffect(() => { if (disabled) setOpen(false); }, [disabled]); useEffect(() => { if (!open) return; const onPointerDown = (event: PointerEvent) => { const target = event.target as Element; if (target instanceof Element && target.closest('[data-testid="dictation-language-menu"]')) return; if (!triggerRef.current?.contains(target)) setOpen(false); }; document.addEventListener("pointerdown", onPointerDown); return () => { document.removeEventListener("pointerdown", onPointerDown); }; }, [open]); const closeOnEscape = (event: ReactKeyboardEvent) => { if (event.key !== "Escape") return; event.preventDefault(); event.stopPropagation(); setOpen(false); triggerRef.current?.focus(); }; const pick = async (code: DictationLanguage) => { if (disabled || saving || language === undefined) return; if (await save(code)) { setOpen(false); triggerRef.current?.focus(); } }; return <> {open && !disabled && {error &&
{error}
} { closeOnEscape(event); // Keep menu navigation out of the composer's submit/shortcut // handlers. cmdk owns selection, scrolling and ARIA state. if (["ArrowDown", "ArrowUp", "Enter", "Home", "End"].includes(event.key)) event.stopPropagation(); }} > No languages match. {results.map((item) => { void pick(item.code); }} asChild > )}
} ; }