Spaces:
				
			
			
	
			
			
		Paused
		
	
	
	
			
			
	
	
	
	
		
		
		Paused
		
	test240527
				/
					web
								
							/app
								
							/components
								
							/base
								
							/features
								
							/feature-panel
								
							/text-to-speech
								
							/param-config-content.tsx
					
			| 'use client' | |
| import useSWR from 'swr' | |
| import produce from 'immer' | |
| import React, { Fragment } from 'react' | |
| import classNames from 'classnames' | |
| import { usePathname } from 'next/navigation' | |
| import { useTranslation } from 'react-i18next' | |
| import { Listbox, Transition } from '@headlessui/react' | |
| import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid' | |
| import { | |
| useFeatures, | |
| useFeaturesStore, | |
| } from '../../hooks' | |
| import type { OnFeaturesChange } from '../../types' | |
| import type { Item } from '@/app/components/base/select' | |
| import { fetchAppVoices } from '@/service/apps' | |
| import Tooltip from '@/app/components/base/tooltip' | |
| import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general' | |
| import { languages } from '@/i18n/language' | |
| type VoiceParamConfigProps = { | |
| onChange?: OnFeaturesChange | |
| } | |
| const VoiceParamConfig = ({ | |
| onChange, | |
| }: VoiceParamConfigProps) => { | |
| const { t } = useTranslation() | |
| const pathname = usePathname() | |
| const matched = pathname.match(/\/app\/([^/]+)/) | |
| const appId = (matched?.length && matched[1]) ? matched[1] : '' | |
| const text2speech = useFeatures(state => state.features.text2speech) | |
| const featuresStore = useFeaturesStore() | |
| const languageItem = languages.find(item => item.value === text2speech.language) | |
| const localLanguagePlaceholder = languageItem?.name || t('common.placeholder.select') | |
| const language = languageItem?.value | |
| const voiceItems = useSWR({ appId, language }, fetchAppVoices).data | |
| const voiceItem = voiceItems?.find(item => item.value === text2speech.voice) | |
| const localVoicePlaceholder = voiceItem?.name || t('common.placeholder.select') | |
| const handleChange = (value: Record<string, string>) => { | |
| const { | |
| features, | |
| setFeatures, | |
| } = featuresStore!.getState() | |
| const newFeatures = produce(features, (draft) => { | |
| draft.text2speech = { | |
| ...draft.text2speech, | |
| ...value, | |
| } | |
| }) | |
| setFeatures(newFeatures) | |
| if (onChange) | |
| onChange(newFeatures) | |
| } | |
| return ( | |
| <div> | |
| <div> | |
| <div className='leading-6 text-base font-semibold text-gray-800'>{t('appDebug.voice.voiceSettings.title')}</div> | |
| <div className='pt-3 space-y-6'> | |
| <div> | |
| <div className='mb-2 flex items-center space-x-1'> | |
| <div className='leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.voice.voiceSettings.language')}</div> | |
| <Tooltip htmlContent={<div className='w-[180px]' > | |
| {t('appDebug.voice.voiceSettings.resolutionTooltip').split('\n').map(item => ( | |
| <div key={item}>{item}</div> | |
| ))} | |
| </div>} selector='config-resolution-tooltip'> | |
| <HelpCircle className='w-[14px] h-[14px] text-gray-400' /> | |
| </Tooltip> | |
| </div> | |
| <Listbox | |
| value={languageItem} | |
| onChange={(value: Item) => { | |
| handleChange({ | |
| language: String(value.value), | |
| }) | |
| }} | |
| > | |
| <div className={'relative h-9'}> | |
| <Listbox.Button className={'w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer'}> | |
| <span className={classNames('block truncate text-left', !languageItem?.name && 'text-gray-400')}> | |
| {languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}`) : localLanguagePlaceholder} | |
| </span> | |
| <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> | |
| <ChevronDownIcon | |
| className="h-5 w-5 text-gray-400" | |
| aria-hidden="true" | |
| /> | |
| </span> | |
| </Listbox.Button> | |
| <Transition | |
| as={Fragment} | |
| leave="transition ease-in duration-100" | |
| leaveFrom="opacity-100" | |
| leaveTo="opacity-0" | |
| > | |
| <Listbox.Options className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm"> | |
| {languages.map((item: Item) => ( | |
| <Listbox.Option | |
| key={item.value} | |
| className={({ active }) => | |
| `relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : '' | |
| }` | |
| } | |
| value={item} | |
| disabled={false} | |
| > | |
| {({ /* active, */ selected }) => ( | |
| <> | |
| <span | |
| className={classNames('block', selected && 'font-normal')}>{t(`common.voice.language.${(item.value).toString().replace('-', '')}`)}</span> | |
| {(selected || item.value === text2speech.language) && ( | |
| <span | |
| className={classNames( | |
| 'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700', | |
| )} | |
| > | |
| <CheckIcon className="h-5 w-5" aria-hidden="true" /> | |
| </span> | |
| )} | |
| </> | |
| )} | |
| </Listbox.Option> | |
| ))} | |
| </Listbox.Options> | |
| </Transition> | |
| </div> | |
| </Listbox> | |
| </div> | |
| <div> | |
| <div className='mb-2 leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.voice.voiceSettings.voice')}</div> | |
| <Listbox | |
| value={voiceItem} | |
| disabled={!languageItem} | |
| onChange={(value: Item) => { | |
| handleChange({ | |
| voice: String(value.value), | |
| }) | |
| }} | |
| > | |
| <div className={'relative h-9'}> | |
| <Listbox.Button className={'w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer'}> | |
| <span className={classNames('block truncate text-left', !voiceItem?.name && 'text-gray-400')}>{voiceItem?.name ?? localVoicePlaceholder}</span> | |
| <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> | |
| <ChevronDownIcon | |
| className="h-5 w-5 text-gray-400" | |
| aria-hidden="true" | |
| /> | |
| </span> | |
| </Listbox.Button> | |
| <Transition | |
| as={Fragment} | |
| leave="transition ease-in duration-100" | |
| leaveFrom="opacity-100" | |
| leaveTo="opacity-0" | |
| > | |
| <Listbox.Options className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm"> | |
| {voiceItems?.map((item: Item) => ( | |
| <Listbox.Option | |
| key={item.value} | |
| className={({ active }) => | |
| `relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : '' | |
| }` | |
| } | |
| value={item} | |
| disabled={false} | |
| > | |
| {({ /* active, */ selected }) => ( | |
| <> | |
| <span className={classNames('block', selected && 'font-normal')}>{item.name}</span> | |
| {(selected || item.value === text2speech.voice) && ( | |
| <span | |
| className={classNames( | |
| 'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700', | |
| )} | |
| > | |
| <CheckIcon className="h-5 w-5" aria-hidden="true" /> | |
| </span> | |
| )} | |
| </> | |
| )} | |
| </Listbox.Option> | |
| ))} | |
| </Listbox.Options> | |
| </Transition> | |
| </div> | |
| </Listbox> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ) | |
| } | |
| export default React.memo(VoiceParamConfig) | |