/** * AI Assistant Hook * @description Vue 3 composable for AI Transcriber functionality */ import { ref, onMounted, onUnmounted } from '../../adapter-vue'; import { TUIStore, StoreName, NAME } from '../../TUICallService/index'; import { TranscriberMessage } from '../../TUICallService/CallService/AIAssistant'; // Translation content interface interface ITranslationContent { language: string; text: string; } // Converted message interface interface ITranslationInfo { roundId: string; sender: string; nick?: string; text: string; end: boolean; translation: ITranslationContent[]; } /** * AI Assistant composable hook * Provides reactive state management for AI transcription functionality */ export function useAIAssistant() { // AI Transcriber Status State const isAITranscriberEnabled = ref(TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED)); const isAITranscriberRunning = ref(TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING)); // Converted message list const subtitleInfoList = ref([]); // Helper functions for message conversion const parseTranslationTexts = (translationTexts: any): ITranslationContent[] => { if (!translationTexts) { return []; } // Handle Map type if (translationTexts instanceof Map) { return Array.from(translationTexts.entries()).map(([language, content]) => ({ language: String(language), text: ensureStringContent(content) })); } // Handle plain object type if (typeof translationTexts === 'object') { return Object.entries(translationTexts).map(([language, content]) => ({ language: String(language), text: ensureStringContent(content) })); } return []; }; const ensureStringContent = (content: any): string => { if (content === null || content === undefined) { return ''; } if (typeof content === 'string') { return content; } if (typeof content === 'object') { try { return JSON.stringify(content); } catch (error) { console.warn('Failed to stringify translation content:', error); return '[Invalid Object]'; } } return String(content); }; const convertToLegacyFormat = (messages: TranscriberMessage[]): ITranslationInfo[] => { return messages.map(msg => ({ roundId: msg.segmentId, sender: msg.speakerUserId, nick: '', text: msg.sourceText, end: msg.isCompleted, translation: parseTranslationTexts(msg.translationTexts) })); }; // Store watcher callbacks const handleAITranscriberEnabledChange = (value: boolean) => { isAITranscriberEnabled.value = value || false; }; const handleAITranscriberRunningChange = (value: boolean) => { isAITranscriberRunning.value = value || false; }; const handleRealtimeMessageListChange = (messageList: TranscriberMessage[]) => { if (messageList && messageList.length > 0) { const convertedMessages = convertToLegacyFormat(messageList); subtitleInfoList.value = convertedMessages; } else { subtitleInfoList.value = []; } }; // Lifecycle management onMounted(() => { // Subscribe to TUIStore changes TUIStore.watch(StoreName.CALL, { [NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange, [NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange, [NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange, }); // Get initial message data const currentMessages = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST); if (currentMessages) { handleRealtimeMessageListChange(currentMessages); } }); onUnmounted(() => { // Unsubscribe from TUIStore changes TUIStore.unwatch(StoreName.CALL, { [NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange, [NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange, [NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange, }); }); return { isAITranscriberEnabled, isAITranscriberRunning, subtitleInfoList, }; }