diff --git a/qwen3-tts-frontend/src/pages/Audiobook.tsx b/qwen3-tts-frontend/src/pages/Audiobook.tsx index da853fb..446da54 100644 --- a/qwen3-tts-frontend/src/pages/Audiobook.tsx +++ b/qwen3-tts-frontend/src/pages/Audiobook.tsx @@ -671,6 +671,23 @@ function ChaptersPanel({ const [editEmoAlpha, setEditEmoAlpha] = useState(0.5) const [savingSegId, setSavingSegId] = useState(null) const [regeneratingSegs, setRegeneratingSegs] = useState>(new Set()) + const [audioVersions, setAudioVersions] = useState>({}) + const prevSegStatusRef = useRef>({}) + + useEffect(() => { + const bumps: Record = {} + segments.forEach(seg => { + if (prevSegStatusRef.current[seg.id] === 'generating' && seg.status === 'done') { + bumps[seg.id] = (audioVersions[seg.id] ?? 0) + 1 + } + }) + if (Object.keys(bumps).length > 0) { + setAudioVersions(prev => ({ ...prev, ...bumps })) + } + const next: Record = {} + segments.forEach(seg => { next[seg.id] = seg.status }) + prevSegStatusRef.current = next + }, [segments]) const startEdit = (seg: AudiobookSegment) => { setEditingSegId(seg.id) @@ -752,7 +769,7 @@ function ChaptersPanel({ {!hasChapters ? (
) : ( -
+
{detail!.chapters.map(ch => { const chSegs = segments.filter(s => s.chapter_index === ch.chapter_index) const chDone = chSegs.filter(s => s.status === 'done').length @@ -768,181 +785,175 @@ function ChaptersPanel({ return next }) return ( -
-
- {chTitle} - {chSegs.length > 0 && ( - - )} -
-
- {ch.status === 'pending' && ( - - )} - {ch.status === 'parsing' && ( -
- - {t('projectCard.chapters.parsing')} -
- )} - {ch.status === 'ready' && !chGenerating && !chAllDone && !generatingChapterIndices.has(ch.chapter_index) && ( - <> - - + + + )} + {ch.status === 'ready' && (chGenerating || generatingChapterIndices.has(ch.chapter_index)) && ( + + {t('projectCard.chapters.segmentProgress', { done: chDone, total: chTotal })} + + )} + {ch.status === 'ready' && chAllDone && ( + <> + {t('projectCard.chapters.doneBadge', { count: chDone })} + + + )} + {ch.status === 'error' && ( + - - )} - {ch.status === 'ready' && (chGenerating || generatingChapterIndices.has(ch.chapter_index)) && ( -
- - {t('projectCard.chapters.segmentProgress', { done: chDone, total: chTotal })} -
- )} - {ch.status === 'ready' && chAllDone && ( - <> - - {t('projectCard.chapters.doneBadge', { count: chDone })} - - - - )} - {ch.status === 'error' && ( - <> - - {ch.error_message && ( - {ch.error_message} - )} - - )} -
+ )} + + + {ch.status === 'parsing' && ( - +
+ +
)} + + {/* Segments — flat list, each is its own card */} {chExpanded && chSegs.length > 0 && ( -
+
{chSegs.map(seg => { const isEditing = editingSegId === seg.id const isRegenerating = regeneratingSegs.has(seg.id) || seg.status === 'generating' const isSaving = savingSegId === seg.id + const audioV = audioVersions[seg.id] ?? 0 return ( -
-
- +
+ {/* Card header */} +
+ {seg.character_name || t('projectCard.segments.unknownCharacter')} {!isEditing && seg.emo_text && ( - {seg.emo_text} + + {seg.emo_text} + {seg.emo_alpha != null && ( + {seg.emo_alpha.toFixed(2)} + )} + )} {isRegenerating && } - {!isRegenerating && seg.status === 'error' && {t('projectCard.segments.errorBadge')}} -
- {!isEditing && ( + {!isRegenerating && seg.status === 'error' && ( + {t('projectCard.segments.errorBadge')} + )} +
+ {!isEditing ? ( <> - - - )} - {isEditing && ( + ) : ( <> - - )}
- {isEditing ? ( -
-