feat: expand intelligent investigation workspace
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
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" ? "女声" : "男声"}`;
|
||||
}
|
||||
Reference in New Issue
Block a user