85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
import type { ReactNode } from "react";
|
||
import { Mic, Volume2 } from "lucide-react";
|
||
|
||
interface VoiceOption {
|
||
id: string;
|
||
name: string;
|
||
}
|
||
|
||
interface VoiceSelectorProps {
|
||
ttsMode: "edgetts" | "voiceclone";
|
||
onSelectTtsMode: (mode: "edgetts" | "voiceclone") => void;
|
||
voices: VoiceOption[];
|
||
voice: string;
|
||
onSelectVoice: (id: string) => void;
|
||
voiceCloneSlot: ReactNode;
|
||
embedded?: boolean;
|
||
}
|
||
|
||
export function VoiceSelector({
|
||
ttsMode,
|
||
onSelectTtsMode,
|
||
voices,
|
||
voice,
|
||
onSelectVoice,
|
||
voiceCloneSlot,
|
||
embedded = false,
|
||
}: VoiceSelectorProps) {
|
||
const content = (
|
||
<>
|
||
<div className="flex gap-2 mb-4">
|
||
<button
|
||
onClick={() => onSelectTtsMode("edgetts")}
|
||
className={`flex-1 py-2 px-2 sm:px-4 rounded-lg text-sm sm:text-base font-medium transition-all flex items-center justify-center gap-1.5 sm:gap-2 ${ttsMode === "edgetts"
|
||
? "bg-purple-600 text-white"
|
||
: "bg-white/10 text-gray-300 hover:bg-white/20"
|
||
}`}
|
||
>
|
||
<Volume2 className="h-4 w-4 shrink-0" />
|
||
选择声音
|
||
</button>
|
||
<button
|
||
onClick={() => onSelectTtsMode("voiceclone")}
|
||
className={`flex-1 py-2 px-2 sm:px-4 rounded-lg text-sm sm:text-base font-medium transition-all flex items-center justify-center gap-1.5 sm:gap-2 ${ttsMode === "voiceclone"
|
||
? "bg-purple-600 text-white"
|
||
: "bg-white/10 text-gray-300 hover:bg-white/20"
|
||
}`}
|
||
>
|
||
<Mic className="h-4 w-4 shrink-0" />
|
||
克隆声音
|
||
</button>
|
||
</div>
|
||
|
||
{ttsMode === "edgetts" && (
|
||
<div className="grid grid-cols-2 gap-3">
|
||
{voices.map((v) => (
|
||
<button
|
||
key={v.id}
|
||
onClick={() => onSelectVoice(v.id)}
|
||
className={`p-3 rounded-xl border-2 transition-all text-left ${voice === v.id
|
||
? "border-purple-500 bg-purple-500/20"
|
||
: "border-white/10 bg-white/5 hover:border-white/30"
|
||
}`}
|
||
>
|
||
<span className="text-white text-sm">{v.name}</span>
|
||
</button>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{ttsMode === "voiceclone" && voiceCloneSlot}
|
||
</>
|
||
);
|
||
|
||
if (embedded) return content;
|
||
|
||
return (
|
||
<div className="bg-white/5 rounded-2xl p-6 border border-white/10 backdrop-blur-sm">
|
||
<h2 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
|
||
🎙️ 配音方式
|
||
</h2>
|
||
{content}
|
||
</div>
|
||
);
|
||
}
|