Files
next-tjwater-frontend/src/features/agent/speech-voices.ts
T
jiang c0e4231b83
Generic Container CI/CD / test-build-publish (push) Successful in 1m52s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m52s
feat: expand intelligent investigation workspace
2026-10-30 17:30:19 +08:00

53 lines
1.9 KiB
TypeScript

import { useSyncExternalStore } from "react";
import { speechVoiceIdSchema, type SpeechVoice } from "@/shared/tts/voices";
export const SPEECH_VOICE_STORAGE_KEY = "tjwater.agent.speech-voice";
const VOICE_CHANGE_EVENT = "tjwater:speech-voice-change";
let memoryVoice = "";
let useMemoryPreference = false;
export function getSelectedSpeechVoice(): string {
let value: string;
try { value = useMemoryPreference ? memoryVoice : localStorage.getItem(SPEECH_VOICE_STORAGE_KEY) ?? ""; }
catch { value = memoryVoice; }
return speechVoiceIdSchema.safeParse(value).success ? value : "";
}
export function setSelectedSpeechVoice(value: string) {
const voice = speechVoiceIdSchema.safeParse(value).success ? value : "";
memoryVoice = voice;
try {
if (voice) localStorage.setItem(SPEECH_VOICE_STORAGE_KEY, voice);
else localStorage.removeItem(SPEECH_VOICE_STORAGE_KEY);
useMemoryPreference = false;
} catch { useMemoryPreference = true; }
window.dispatchEvent(new Event(VOICE_CHANGE_EVENT));
}
function subscribe(onChange: () => void) {
window.addEventListener(VOICE_CHANGE_EVENT, onChange);
window.addEventListener("storage", onChange);
return () => {
window.removeEventListener(VOICE_CHANGE_EVENT, onChange);
window.removeEventListener("storage", onChange);
};
}
export function useSelectedSpeechVoice() {
return useSyncExternalStore(subscribe, getSelectedSpeechVoice, () => "");
}
const chineseNames: Record<string, string> = {
"zh-CN-XiaoxiaoNeural": "晓晓", "zh-CN-XiaoyiNeural": "晓伊",
"zh-CN-YunxiNeural": "云希", "zh-CN-YunyangNeural": "云扬",
"zh-CN-YunjianNeural": "云健", "zh-CN-YunxiaNeural": "云夏"
};
export function getSpeechVoiceName(voice: SpeechVoice) {
return chineseNames[voice.id] ?? voice.id.replace(`${voice.locale}-`, "").replace(/Neural$/, "");
}
export function getSpeechVoiceDescription(voice: SpeechVoice) {
return `普通话 · ${voice.gender === "Female" ? "女声" : "男声"}`;
}