'use client'
import type { ReactNode } from 'react'
import { useTranslations } from 'next-intl'
import TaskStatusInline from '@/components/task/TaskStatusInline'
import { AppIcon } from '@/components/ui/icons'
import type { TaskPresentationState } from '@/lib/task/presentation'
import {
MAX_VOICE_SCHEME_COUNT,
MIN_VOICE_SCHEME_COUNT,
normalizeVoiceSchemeCount,
type GeneratedVoice,
} from './voice-design-shared'
const VOICE_PRESET_KEYS = [
'maleBroadcaster',
'gentleFemale',
'matureMale',
'livelyFemale',
'intellectualFemale',
'narrator',
] as const
type VoicePresetKey = (typeof VOICE_PRESET_KEYS)[number]
interface VoiceDesignGeneratorSectionProps {
voicePrompt: string
onVoicePromptChange: (value: string) => void
previewText: string
onPreviewTextChange: (value: string) => void
schemeCount: string
onSchemeCountChange: (value: string) => void
isSubmitting: boolean
submittingState: TaskPresentationState | null
error: string | null
generatedVoices: GeneratedVoice[]
selectedIndex: number | null
onSelectIndex: (index: number) => void
playingIndex: number | null
onPlayVoice: (index: number) => void
onGenerate: () => void
footer?: ReactNode
}
export default function VoiceDesignGeneratorSection({
voicePrompt,
onVoicePromptChange,
previewText,
onPreviewTextChange,
schemeCount,
onSchemeCountChange,
isSubmitting,
submittingState,
error,
generatedVoices,
selectedIndex,
onSelectIndex,
playingIndex,
onPlayVoice,
onGenerate,
footer = null,
}: VoiceDesignGeneratorSectionProps) {
const tv = useTranslations('voice.voiceDesign')
const normalizedSchemeCount = normalizeVoiceSchemeCount(schemeCount)
return (
<>
{tv('selectStyle')}
{VOICE_PRESET_KEYS.map((presetKey) => {
const prompt = tv(`presetsPrompts.${presetKey}` as `presetsPrompts.${VoicePresetKey}`)
return (
)
})}
{tv('orCustomDescription')}
{tv('editPreviewText')}
onPreviewTextChange(event.target.value)}
placeholder={tv('defaultPreviewText')}
className="glass-input-base w-full mt-2 px-3 py-2 text-sm"
/>
{generatedVoices.length === 0 && !isSubmitting && (
{
if (!voicePrompt.trim()) return
onGenerate()
}}
onKeyDown={(event) => {
if (!voicePrompt.trim()) return
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
onGenerate()
}
}}
className={`glass-btn-base glass-btn-primary w-full py-2.5 rounded-lg text-sm font-medium transition-opacity ${
!voicePrompt.trim() ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
}`}
>
{tv('generateSchemesPrefix')}
event.stopPropagation()}
onKeyDown={(event) => event.stopPropagation()}
>
{tv('generateSchemesSuffix')}
)}
{isSubmitting && submittingState && (
)}
{generatedVoices.length > 0 && (
{tv('selectScheme')}
{generatedVoices.map((voice, index) => (
onSelectIndex(index)}
className={`relative p-3 rounded-lg border-2 cursor-pointer transition-all text-center ${
selectedIndex === index
? 'border-[var(--glass-stroke-focus)] bg-[var(--glass-tone-info-bg)]'
: 'border-[var(--glass-stroke-base)] hover:border-[var(--glass-stroke-focus)]'
}`}
>
{selectedIndex === index && (
)}
{tv('schemeN', { n: index + 1 })}
))}
{footer}
)}
{error && (
{error}
)}
>
)
}