Spaces:
Running
Running
File size: 2,084 Bytes
cbe4c9d |
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 |
import { Select, Stack, Text } from "@mantine/core";
import { useForm } from "@mantine/form";
import getUnicodeFlagIcon from "country-flag-icons/unicode";
import { usePubSub } from "create-pubsub/react";
import { useCallback, useEffect, useState } from "react";
import { settingsPubSub } from "../../../../modules/pubSub";
export default function VoiceSettingsForm() {
const [settings, setSettings] = usePubSub(settingsPubSub);
const [voices, setVoices] = useState<{ value: string; label: string }[]>([]);
const getCountryFlag = useCallback((langCode: string) => {
try {
const country = langCode.split("-")[1];
if (country.length !== 2) throw new Error("Invalid country code");
return getUnicodeFlagIcon(country);
} catch {
return "π";
}
}, []);
const form = useForm({
initialValues: settings,
onValuesChange: setSettings,
});
useEffect(() => {
const updateVoices = () => {
const availableVoices = self.speechSynthesis.getVoices();
const uniqueVoices = Array.from(
new Map(
availableVoices.map((voice) => [voice.voiceURI, voice]),
).values(),
);
const voiceOptions = uniqueVoices
.sort((a, b) => a.lang.localeCompare(b.lang))
.map((voice) => ({
value: voice.voiceURI,
label: `${getCountryFlag(voice.lang)} ${voice.name} β’ ${voice.lang}`,
}));
setVoices(voiceOptions);
};
updateVoices();
self.speechSynthesis.onvoiceschanged = updateVoices;
return () => {
self.speechSynthesis.onvoiceschanged = null;
};
}, [getCountryFlag]);
return (
<Stack gap="xs">
<Text size="sm">Voice Selection</Text>
<Text size="xs" c="dimmed">
Choose the voice to use when reading AI responses aloud.
</Text>
<Select
{...form.getInputProps("selectedVoiceId")}
data={voices}
searchable
nothingFoundMessage="No voices found"
placeholder="Auto-detected"
allowDeselect={true}
clearable
/>
</Stack>
);
}
|