feat: enhance emotion handling in audiobook segments and UI for multi-emotion selection

This commit is contained in:
2026-03-13 14:39:51 +08:00
parent 16947d6b8b
commit 161e7fa76d
4 changed files with 37 additions and 23 deletions

View File

@@ -1381,9 +1381,7 @@ function CharactersPanel({
}
const EMOTION_OPTIONS = ['开心', '愤怒', '悲伤', '恐惧', '厌恶', '低沉', '惊讶', '中性']
const EMOTION_ALPHA_DEFAULTS: Record<string, number> = {
开心: 0.6, 愤怒: 0.15, 悲伤: 0.4, 恐惧: 0.4, 厌恶: 0.6, 低沉: 0.6, 惊讶: 0.3, 中性: 0.5,
}
function ChaptersPanel({
project,
@@ -1674,8 +1672,10 @@ function ChaptersPanel({
{seg.character_name || t('projectCard.segments.unknownCharacter')}
</Badge>
{!isEditing && seg.emo_text && (
<span className="text-[11px] text-muted-foreground shrink-0">
{seg.emo_text}
<span className="text-[11px] text-muted-foreground shrink-0 flex items-center gap-0.5 flex-wrap">
{seg.emo_text.split('+').map(e => (
<span key={e} className="bg-muted rounded px-1">{e.trim()}</span>
))}
{seg.emo_alpha != null && (
<span className="opacity-60 ml-0.5">{seg.emo_alpha.toFixed(2)}</span>
)}
@@ -1718,22 +1718,32 @@ function ChaptersPanel({
className="text-sm min-h-[60px] resize-y"
rows={3}
/>
<div className="flex items-center gap-2 flex-wrap">
<span className="text-xs text-muted-foreground shrink-0">{t('projectCard.segments.emotion')}:</span>
<select
value={editEmoText}
onChange={e => {
const v = e.target.value
setEditEmoText(v)
if (v && EMOTION_ALPHA_DEFAULTS[v]) setEditEmoAlpha(EMOTION_ALPHA_DEFAULTS[v])
}}
className="text-xs h-6 rounded border border-input bg-background px-1 focus:outline-none"
>
<option value="">{t('projectCard.segments.noEmotion')}</option>
{EMOTION_OPTIONS.map(e => <option key={e} value={e}>{e}</option>)}
</select>
<div className="space-y-1.5">
<div className="flex items-center gap-1 flex-wrap">
<span className="text-xs text-muted-foreground shrink-0">{t('projectCard.segments.emotion')}:</span>
{EMOTION_OPTIONS.map(emo => {
const selectedEmos = editEmoText.split('+').filter(Boolean)
const isSelected = selectedEmos.includes(emo)
return (
<button
key={emo}
type="button"
className={`px-2 py-0.5 rounded text-xs border transition-colors ${isSelected ? "bg-primary text-primary-foreground border-primary" : "bg-muted text-muted-foreground border-transparent"}`}
onClick={() => {
const current = editEmoText.split('+').filter(Boolean)
const next = isSelected
? current.filter(e => e !== emo)
: [...current, emo]
setEditEmoText(next.join('+'))
}}
>
{emo}
</button>
)
})}
</div>
{editEmoText && (
<div className="flex items-center gap-1.5 flex-1 min-w-[120px]">
<div className="flex items-center gap-1.5">
<span className="text-xs text-muted-foreground shrink-0">{t('projectCard.segments.intensity')}:</span>
<input
type="range"