Co-authored-by: luowei <glpat-EjySCyNjWiLqAED-YmwM> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>tags/0.5.7
| import ConfigContext from '@/context/debug-configuration' | import ConfigContext from '@/context/debug-configuration' | ||||
| import { languages } from '@/utils/language' | import { languages } from '@/utils/language' | ||||
| import { fetchAppVoices } from '@/service/apps' | import { fetchAppVoices } from '@/service/apps' | ||||
| import AudioBtn from '@/app/components/base/audio-btn' | |||||
| const TextToSpeech: FC = () => { | const TextToSpeech: FC = () => { | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| const matched = pathname.match(/\/app\/([^/]+)/) | const matched = pathname.match(/\/app\/([^/]+)/) | ||||
| const appId = (matched?.length && matched[1]) ? matched[1] : '' | const appId = (matched?.length && matched[1]) ? matched[1] : '' | ||||
| const language = textToSpeechConfig.language | const language = textToSpeechConfig.language | ||||
| const languageInfo = languages.find(i => i.value === textToSpeechConfig.language) | |||||
| const voiceItems = useSWR({ appId, language }, fetchAppVoices).data | const voiceItems = useSWR({ appId, language }, fetchAppVoices).data | ||||
| const voiceItem = voiceItems?.find(item => item.value === textToSpeechConfig.voice) | const voiceItem = voiceItems?.find(item => item.value === textToSpeechConfig.voice) | ||||
| return ( | return ( | ||||
| <Panel | <Panel | ||||
| title={ | title={ | ||||
| <div className='flex items-center gap-2'> | |||||
| <div className='flex items-center'> | |||||
| <div>{t('appDebug.feature.textToSpeech.title')}</div> | <div>{t('appDebug.feature.textToSpeech.title')}</div> | ||||
| </div> | </div> | ||||
| } | } | ||||
| headerIcon={<Speaker className='w-4 h-4 text-[#7839EE]' />} | headerIcon={<Speaker className='w-4 h-4 text-[#7839EE]' />} | ||||
| headerRight={ | headerRight={ | ||||
| <div className='text-xs text-gray-500'> | |||||
| {languages.find(i => i.value === textToSpeechConfig.language)?.name} - {voiceItem?.name ?? t('appDebug.voice.defaultDisplay')} | |||||
| <div className='text-xs text-gray-500 inline-flex items-center gap-2'> | |||||
| {languageInfo && (`${languageInfo?.name} - `)}{voiceItem?.name ?? t('appDebug.voice.defaultDisplay')} | |||||
| { languageInfo?.example && ( | |||||
| <AudioBtn | |||||
| value={languageInfo?.example} | |||||
| isAudition={true} | |||||
| /> | |||||
| )} | |||||
| </div> | </div> | ||||
| } | } | ||||
| noBodySpacing | noBodySpacing |
| type AudioBtnProps = { | type AudioBtnProps = { | ||||
| value: string | value: string | ||||
| className?: string | className?: string | ||||
| isAudition?: boolean | |||||
| } | } | ||||
| const AudioBtn = ({ | const AudioBtn = ({ | ||||
| value, | value, | ||||
| className, | className, | ||||
| isAudition, | |||||
| }: AudioBtnProps) => { | }: AudioBtnProps) => { | ||||
| const audioRef = useRef<HTMLAudioElement | null>(null) | const audioRef = useRef<HTMLAudioElement | null>(null) | ||||
| const [isPlaying, setIsPlaying] = useState(false) | const [isPlaying, setIsPlaying] = useState(false) | ||||
| className='z-10' | className='z-10' | ||||
| > | > | ||||
| <div | <div | ||||
| className={'box-border p-0.5 flex items-center justify-center rounded-md bg-white cursor-pointer'} | |||||
| style={{ boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' }} | |||||
| className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || 'rounded-md bg-white'}`} | |||||
| style={{ boxShadow: !isAudition ? '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' : '' }} | |||||
| onClick={togglePlayPause}> | onClick={togglePlayPause}> | ||||
| <div className={`w-6 h-6 rounded-md hover:bg-gray-50 ${!isPause ? ((isPlaying && !hasEnded) ? s.playIcon : s.stopIcon) : s.pauseIcon}`}></div> | |||||
| <div className={`w-6 h-6 rounded-md ${!isAudition ? 'hover:bg-gray-200' : 'hover:bg-gray-50'} ${!isPause ? ((isPlaying && !hasEnded) ? s.playIcon : s.stopIcon) : s.pauseIcon}`}></div> | |||||
| </div> | </div> | ||||
| </Tooltip> | </Tooltip> | ||||
| </div> | </div> |
| koKR: 'Korean', | koKR: 'Korean', | ||||
| ptBR: 'Portuguese', | ptBR: 'Portuguese', | ||||
| ruRU: 'Russian', | ruRU: 'Russian', | ||||
| ukUA: 'Ukrainian', | |||||
| }, | }, | ||||
| }, | }, | ||||
| unit: { | unit: { |
| itIT: 'italiano', | itIT: 'italiano', | ||||
| thTH: 'tailandês', | thTH: 'tailandês', | ||||
| idID: 'indonésio', | idID: 'indonésio', | ||||
| ukUA: 'ucraniana', | |||||
| }, | }, | ||||
| }, | }, | ||||
| provider: { | provider: { |
| koKR: '韩语', | koKR: '韩语', | ||||
| ptBR: '葡萄牙语', | ptBR: '葡萄牙语', | ||||
| ruRU: '俄语', | ruRU: '俄语', | ||||
| ukUA: '乌克兰语', | |||||
| }, | }, | ||||
| }, | }, | ||||
| unit: { | unit: { |
| export type Item = { | export type Item = { | ||||
| value: number | string | value: number | string | ||||
| name: string | name: string | ||||
| example: string | |||||
| } | } | ||||
| export const LanguagesSupported = ['en-US', 'zh-Hans', 'pt-BR', 'es-ES', 'fr-FR', 'de-DE', 'ja-JP', 'ko-KR', 'ru-RU', 'it-IT', 'th-TH', 'id-ID', 'uk-UA'] | export const LanguagesSupported = ['en-US', 'zh-Hans', 'pt-BR', 'es-ES', 'fr-FR', 'de-DE', 'ja-JP', 'ko-KR', 'ru-RU', 'it-IT', 'th-TH', 'id-ID', 'uk-UA'] | ||||
| { | { | ||||
| value: 'en-US', | value: 'en-US', | ||||
| name: 'English(United States)', | name: 'English(United States)', | ||||
| example: 'Hello, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'zh-Hans', | value: 'zh-Hans', | ||||
| name: '简体中文', | name: '简体中文', | ||||
| example: '你好,Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'pt-BR', | value: 'pt-BR', | ||||
| name: 'Português(Brasil)', | name: 'Português(Brasil)', | ||||
| example: 'Olá, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'es-ES', | value: 'es-ES', | ||||
| name: 'Español(España)', | name: 'Español(España)', | ||||
| example: 'Saluton, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'fr-FR', | value: 'fr-FR', | ||||
| name: 'Français(France)', | name: 'Français(France)', | ||||
| example: 'Bonjour, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'de-DE', | value: 'de-DE', | ||||
| name: 'Deutsch(Deutschland)', | name: 'Deutsch(Deutschland)', | ||||
| example: 'Hallo, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'ja-JP', | value: 'ja-JP', | ||||
| name: '日本語(日本)', | name: '日本語(日本)', | ||||
| example: 'こんにちは、Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'ko-KR', | value: 'ko-KR', | ||||
| name: '한국어(대한민국)', | name: '한국어(대한민국)', | ||||
| example: '안녕, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'ru-RU', | value: 'ru-RU', | ||||
| name: 'Русский(Россия)', | name: 'Русский(Россия)', | ||||
| example: ' Привет, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'it-IT', | value: 'it-IT', | ||||
| name: 'Italiano(Italia)', | name: 'Italiano(Italia)', | ||||
| example: 'Ciao, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'th-TH', | value: 'th-TH', | ||||
| name: 'ไทย(ประเทศไทย)', | name: 'ไทย(ประเทศไทย)', | ||||
| example: 'สวัสดี Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'id-ID', | value: 'id-ID', | ||||
| name: 'Bahasa Indonesia', | name: 'Bahasa Indonesia', | ||||
| example: 'Saluto, Dify!', | |||||
| }, | }, | ||||
| { | { | ||||
| value: 'uk-UA', | value: 'uk-UA', | ||||
| name: 'Українська(Україна)', | name: 'Українська(Україна)', | ||||
| example: 'Привет, Dify!', | |||||
| }, | }, | ||||
| ] | ] | ||||