diff --git a/.env.example b/.env.example index 4c59e04..8cce32f 100644 --- a/.env.example +++ b/.env.example @@ -7,8 +7,8 @@ VITE_SERVE_PROXY=xxx # Aruze tts Key (required for tts) VITE_SCRIPTION_KEY=xxx -# Aruze tts Region (required for tts , e.g eastasia1) -VITE_REGION=xxx +# # Aruze tts Region (required for tts , e.g eastasia1) 已经不需要了,直接在界面中指定 +# VITE_REGION=xxx # Aruze translate Key (required for translate) VITE_TRANSLATE_KEY=xxx diff --git a/components.d.ts b/components.d.ts index efe4863..0a3f857 100644 --- a/components.d.ts +++ b/components.d.ts @@ -9,6 +9,7 @@ export {} declare module '@vue/runtime-core' { export interface GlobalComponents { + Avatar: typeof import('./src/components/Avatar.vue')['default'] Button: typeof import('./src/components/Button.vue')['default'] Card: typeof import('./src/pages/Home/components/Card.vue')['default'] Content: typeof import('./src/pages/Home/Content.vue')['default'] diff --git a/electron/main/index.ts b/electron/main/index.ts index 4d7c394..23ab21b 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -78,7 +78,7 @@ ipcMain.on('open-settings-window', (event) => { y: 200, frame: true, titleBarStyle: 'default', - modal: true, // 模态窗口,会阻塞父窗口 (macOS 不支持) + // modal: true, // 模态窗口,会阻塞父窗口 (macOS 不支持) parent: win!, resizable: false, fullscreenable: false, diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts index 5d21739..63a7427 100644 --- a/src/auto-imports.d.ts +++ b/src/auto-imports.d.ts @@ -173,6 +173,7 @@ declare global { const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] const useGamepad: typeof import('@vueuse/core')['useGamepad'] const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] + const useGlobalSetting: typeof import('./hooks/useGlobalSetting')['default'] const useIdle: typeof import('@vueuse/core')['useIdle'] const useImage: typeof import('@vueuse/core')['useImage'] const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll'] @@ -456,6 +457,7 @@ declare module 'vue' { readonly useFullscreen: UnwrapRef readonly useGamepad: UnwrapRef readonly useGeolocation: UnwrapRef + readonly useGlobalSetting: UnwrapRef readonly useIdle: UnwrapRef readonly useImage: UnwrapRef readonly useInfiniteScroll: UnwrapRef diff --git a/src/components/Avatar.vue b/src/components/Avatar.vue new file mode 100644 index 0000000..758ff18 --- /dev/null +++ b/src/components/Avatar.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/constant.ts b/src/constant.ts index 9e2a7ad..f928711 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -8,3 +8,4 @@ export const IS_ALWAYS_RECOGNITION = 'isAlwaysRecognition' export const OPEN_MODEL = 'openModel' export const CHAT_API_NAME = 'chatApiName' export const CHAT_REMEMBER_COUNT = 'chatRememberCount' +export const SELF_AVATAR_URL = 'selfAvatarUrl' diff --git a/src/hooks/useGlobalSetting.ts b/src/hooks/useGlobalSetting.ts new file mode 100644 index 0000000..2d65eb3 --- /dev/null +++ b/src/hooks/useGlobalSetting.ts @@ -0,0 +1,37 @@ +import { AZURE_KEY, AZURE_REGION, AZURE_TRANSLATE_KEY, CHAT_API_NAME, CHAT_REMEMBER_COUNT, OPEN_KEY, OPEN_MODEL, OPEN_PROXY, SELF_AVATAR_URL } from '@/constant' + +import { getAvatarUrl } from '@/utils' + +const defaultOpenKey = import.meta.env.VITE_OPENAI_API_KEY +const defaultOpenProxy = import.meta.env.VITE_SERVE_PROXY +const defaultAzureRegion = import.meta.env.VITE_REGION +const defaultAzureKey = import.meta.env.VITE_SCRIPTION_KEY +const defaultAzureTranslateKey = import.meta.env.VITE_TRANSLATE_KEY + +export const useGlobalSetting = () => { + console.log('useGlobalSetting') + + const openKey = useLocalStorage(OPEN_KEY, defaultOpenKey) + const openProxy = useLocalStorage(OPEN_PROXY, defaultOpenProxy) + const azureRegion = useLocalStorage(AZURE_REGION, defaultAzureRegion) + const azureKey = useLocalStorage(AZURE_KEY, defaultAzureKey) + const azureTranslateKey = useLocalStorage(AZURE_TRANSLATE_KEY, defaultAzureTranslateKey) + const openModel = useLocalStorage(OPEN_MODEL, 'gpt-3.5-turbo') + const selfAvatar = useLocalStorage(SELF_AVATAR_URL, getAvatarUrl('self.png')) + const chatApiName = useLocalStorage(CHAT_API_NAME, 'openai') + const chatRememberCount = useLocalStorage(CHAT_REMEMBER_COUNT, '10') + + return { + openKey, + openProxy, + openModel, + azureRegion, + azureKey, + azureTranslateKey, + selfAvatar, + chatApiName, + chatRememberCount, + } +} + +export default useGlobalSetting diff --git a/src/hooks/useSpeechService.ts b/src/hooks/useSpeechService.ts index c85ee42..6ab2c99 100644 --- a/src/hooks/useSpeechService.ts +++ b/src/hooks/useSpeechService.ts @@ -1,20 +1,20 @@ import type { VoiceInfo } from 'microsoft-cognitiveservices-speech-sdk' import { AudioConfig, - ResultReason, SpeakerAudioDestination, SpeechConfig, SpeechRecognizer, SpeechSynthesizer, } from 'microsoft-cognitiveservices-speech-sdk' -export const useSpeechService = (subscriptionKey: string, region: string, langs = ['fr-FR', 'ja-JP', 'en-US', 'zh-CN', 'zh-HK', 'ko-KR', 'de-DE']) => { +export const useSpeechService = (langs = ['fr-FR', 'ja-JP', 'en-US', 'zh-CN', 'zh-HK', 'ko-KR', 'de-DE']) => { + const { azureKey, azureRegion } = useGlobalSetting() const languages = ref(langs) const language = ref(langs[0]) const languageMap = ref>>({}) const voiceName = ref('en-US-JennyMultilingualNeural') - const speechConfig = ref(SpeechConfig.fromSubscription(subscriptionKey, region)) + const speechConfig = ref(SpeechConfig.fromSubscription(azureKey.value, azureRegion.value)) const isRecognizing = ref(false) // 语音识别中 const isSynthesizing = ref(false) // 语音合成中 const isPlaying = ref(false) // 语音播放中 @@ -31,11 +31,11 @@ export const useSpeechService = (subscriptionKey: string, region: string, langs // 引入变量,触发 SpeechSynthesizer 实例的重新创建 const count = ref(0) - watch([language, voiceName, count], ([lang, voice]) => { + watch([language, voiceName, count, azureKey, azureRegion], ([lang, voice]) => { + speechConfig.value = SpeechConfig.fromSubscription(azureKey.value, azureRegion.value) speechConfig.value.speechRecognitionLanguage = lang speechConfig.value.speechSynthesisLanguage = lang speechConfig.value.speechSynthesisVoiceName = voice - // 通过playback结束事件来判断播放结束 const player = new SpeakerAudioDestination() player.onAudioStart = function (_) { @@ -48,6 +48,7 @@ export const useSpeechService = (subscriptionKey: string, region: string, langs isPlayend.value = true console.log('playback finished') } + const audioConfig = AudioConfig.fromDefaultMicrophoneInput() const audioConfiga = AudioConfig.fromSpeakerOutput(player) recognizer.value = new SpeechRecognizer(speechConfig.value, audioConfig) diff --git a/src/pages/Home/components/Content.vue b/src/pages/Home/components/Content.vue index 5dd624a..5a50327 100644 --- a/src/pages/Home/components/Content.vue +++ b/src/pages/Home/components/Content.vue @@ -1,7 +1,7 @@