Implement voice assignment component (Task 10)
- Created VoiceAssignmentSection component with role-to-voice mapping - Added language selector with American English, British English, French, Japanese - Added speed slider (0.5x - 1.5x range) - Implemented Generate Audiobook button with proper disabled state - Integrated component into App.tsx with state management - Added role detection API call on text submission - Validates all roles have assigned voices before enabling generation Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 4.1, 4.2, 4.3, 4.4, 5.1, 5.2, 5.3
This commit is contained in:
@@ -1,27 +1,102 @@
|
||||
import { useState } from 'react'
|
||||
import './App.css'
|
||||
import TextInputSection from './components/TextInputSection'
|
||||
import VoiceAssignmentSection from './components/VoiceAssignmentSection'
|
||||
import type { Role } from './api/tts'
|
||||
import { detectRoles } from './api/tts'
|
||||
|
||||
function App() {
|
||||
const [text, setText] = useState('')
|
||||
const [phase, setPhase] = useState<'input' | 'assignment' | 'generated'>('input')
|
||||
const [isLoading] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [roles, setRoles] = useState<Role[]>([])
|
||||
const [voiceAssignments, setVoiceAssignments] = useState<Map<string, string>>(new Map())
|
||||
const [language, setLanguage] = useState('a') // Default: American English
|
||||
const [speed, setSpeed] = useState(1.0) // Default: 1.0x
|
||||
const [isGenerating, setIsGenerating] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const handleTextChange = (newText: string) => {
|
||||
setText(newText)
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
// TODO: Implement role detection and phase transition
|
||||
console.log('Moving to voice assignment phase with text:', text)
|
||||
setPhase('assignment')
|
||||
const handleNext = async () => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
// Detect roles in the text
|
||||
const response = await detectRoles(text)
|
||||
setRoles(response.roles)
|
||||
|
||||
// Initialize voice assignments map (empty initially)
|
||||
setVoiceAssignments(new Map())
|
||||
|
||||
// Move to assignment phase
|
||||
setPhase('assignment')
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to detect roles')
|
||||
console.error('Role detection error:', err)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleVoiceChange = (roleId: string, voiceId: string) => {
|
||||
setVoiceAssignments(prev => {
|
||||
const newMap = new Map(prev)
|
||||
newMap.set(roleId, voiceId)
|
||||
return newMap
|
||||
})
|
||||
}
|
||||
|
||||
const handleLanguageChange = (lang: string) => {
|
||||
setLanguage(lang)
|
||||
}
|
||||
|
||||
const handleSpeedChange = (newSpeed: number) => {
|
||||
setSpeed(newSpeed)
|
||||
}
|
||||
|
||||
const handleGenerate = async () => {
|
||||
setIsGenerating(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
// TODO: Implement audio generation
|
||||
console.log('Generating audiobook with:', {
|
||||
text,
|
||||
language,
|
||||
voiceAssignments: Array.from(voiceAssignments.entries()),
|
||||
speed
|
||||
})
|
||||
|
||||
// Placeholder - will be implemented in later tasks
|
||||
setTimeout(() => {
|
||||
setIsGenerating(false)
|
||||
setPhase('generated')
|
||||
}, 1000)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to generate audiobook')
|
||||
console.error('Generation error:', err)
|
||||
setIsGenerating(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all roles have assigned voices
|
||||
const allRolesAssigned = roles.length > 0 && roles.every(role => voiceAssignments.has(role.id))
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="container mx-auto py-8">
|
||||
<h1 className="text-4xl font-bold text-center mb-8">Text to Audiobook</h1>
|
||||
|
||||
{error && (
|
||||
<div className="max-w-4xl mx-auto mb-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{phase === 'input' && (
|
||||
<TextInputSection
|
||||
text={text}
|
||||
@@ -33,9 +108,18 @@ function App() {
|
||||
)}
|
||||
|
||||
{phase === 'assignment' && (
|
||||
<div className="text-center">
|
||||
<p>Voice assignment phase (to be implemented)</p>
|
||||
</div>
|
||||
<VoiceAssignmentSection
|
||||
roles={roles}
|
||||
voiceAssignments={voiceAssignments}
|
||||
onVoiceChange={handleVoiceChange}
|
||||
language={language}
|
||||
onLanguageChange={handleLanguageChange}
|
||||
speed={speed}
|
||||
onSpeedChange={handleSpeedChange}
|
||||
onGenerate={handleGenerate}
|
||||
isGenerating={isGenerating}
|
||||
allRolesAssigned={allRolesAssigned}
|
||||
/>
|
||||
)}
|
||||
|
||||
{phase === 'generated' && (
|
||||
|
||||
Reference in New Issue
Block a user