@trtc/calls-uikit-react 4.4.3 → 4.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/package.json +2 -2
  2. package/src/Components/assets/aiAssistant/desktop/subtitleSettings.svg +4 -0
  3. package/src/Components/assets/aiAssistant/mobile/close-aiAssistant.svg +7 -0
  4. package/src/Components/assets/aiAssistant/mobile/open-aiAssistant.svg +7 -0
  5. package/src/Components/assets/aiAssistant/mobile/subtitleSettings.svg +3 -0
  6. package/src/Components/components/base/CustomSelect/CustomSelect.scss +116 -0
  7. package/src/Components/components/base/CustomSelect/CustomSelect.tsx +96 -0
  8. package/src/Components/components/common/AIAssistant/AISubtitle.scss +109 -0
  9. package/src/Components/components/common/AIAssistant/AISubtitle.tsx +52 -66
  10. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.tsx +39 -0
  11. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.scss +46 -0
  12. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.tsx +42 -0
  13. package/src/Components/components/common/AIAssistant/components/SubtitleContent.scss +67 -0
  14. package/src/Components/components/common/AIAssistant/components/SubtitleContent.tsx +134 -0
  15. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.scss +275 -0
  16. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.tsx +265 -0
  17. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.scss +98 -0
  18. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.tsx +198 -0
  19. package/src/Components/components/common/AIAssistant/components/index.ts +5 -0
  20. package/src/Components/components/common/AIAssistant/index.ts +3 -1
  21. package/src/Components/components/common/TopBar/h5/TopBarH5.tsx +11 -1
  22. package/src/Components/components/common/TopBar/pc/TopBarPC.module.scss +20 -0
  23. package/src/Components/components/common/TopBar/pc/TopBarPC.tsx +14 -9
  24. package/src/Components/hooks/index.ts +2 -0
  25. package/src/Components/hooks/useAIAssistant.ts +174 -0
  26. package/src/TUICallService/CallService/AIAssistant.ts +285 -39
  27. package/src/TUICallService/CallService/index.ts +19 -9
  28. package/src/TUICallService/TUIStore/callStore.ts +5 -0
  29. package/src/TUICallService/const/index.ts +4 -0
  30. package/src/TUICallService/interface/ICallStore.ts +5 -0
  31. package/src/TUICallService/locales/en.ts +15 -0
  32. package/src/TUICallService/locales/ja_JP.ts +15 -0
  33. package/src/TUICallService/locales/zh-cn.ts +15 -0
  34. package/src/index.ts +1 -1
  35. package/tuicall-uikit-react.es.js +4265 -3703
  36. package/tuicall-uikit-react.umd.js +8 -3
  37. package/types/Components/components/base/CustomSelect/CustomSelect.d.ts +15 -0
  38. package/types/Components/components/common/AIAssistant/AISubtitle.d.ts +1 -0
  39. package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.d.ts +3 -0
  40. package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.d.ts +4 -0
  41. package/types/Components/components/common/AIAssistant/components/SubtitleContent.d.ts +8 -0
  42. package/types/Components/components/common/AIAssistant/components/SubtitleSettingsH5.d.ts +13 -0
  43. package/types/Components/components/common/AIAssistant/components/SubtitleSettingsPC.d.ts +13 -0
  44. package/types/Components/components/common/AIAssistant/components/index.d.ts +5 -0
  45. package/types/Components/components/common/AIAssistant/index.d.ts +1 -0
  46. package/types/Components/hooks/index.d.ts +2 -1
  47. package/types/Components/hooks/useAIAssistant.d.ts +22 -0
  48. package/types/TUICallService/CallService/AIAssistant.d.ts +72 -15
  49. package/types/TUICallService/CallService/index.d.ts +2 -1
  50. package/types/TUICallService/interface/ICallStore.d.ts +4 -0
  51. package/types/TUICallService/locales/en.d.ts +14 -0
  52. package/types/TUICallService/locales/ja_JP.d.ts +14 -0
  53. package/types/TUICallService/locales/zh-cn.d.ts +14 -0
@@ -1,9 +1,11 @@
1
1
  import useTranslate from "./useTranslate";
2
2
  import useDeviceList from "./useDeviceList";
3
3
  import useCallDuration from "./useCallDuration";
4
+ import { useAIAssistant } from "./useAIAssistant";
4
5
 
5
6
  export {
6
7
  useTranslate,
7
8
  useDeviceList,
8
9
  useCallDuration,
10
+ useAIAssistant,
9
11
  }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * AI Assistant Hook
3
+ * @description React hook for AI Transcriber functionality
4
+ */
5
+ import { useState, useEffect, useCallback } from 'react';
6
+ import { TUIStore, StoreName, NAME } from '../../TUICallService/index';
7
+
8
+ // Translation content interface
9
+ interface ITranslationContent {
10
+ language: string;
11
+ text: string;
12
+ }
13
+
14
+ // Transcriber message interface (simplified for React version)
15
+ interface TranscriberMessage {
16
+ segmentId: string;
17
+ speakerUserId: string;
18
+ sourceText: string;
19
+ isCompleted: boolean;
20
+ translationTexts: any;
21
+ }
22
+
23
+ // Converted message interface
24
+ interface ITranslationInfo {
25
+ roundId: string;
26
+ sender: string;
27
+ nick?: string;
28
+ text: string;
29
+ end: boolean;
30
+ translation: ITranslationContent[];
31
+ }
32
+
33
+ /**
34
+ * AI Assistant hook
35
+ * Provides state management for AI transcription functionality
36
+ */
37
+ export function useAIAssistant() {
38
+ // AI Transcriber Status State
39
+ const [isAITranscriberEnabled, setIsAITranscriberEnabled] = useState<boolean>(false);
40
+ const [isAITranscriberRunning, setIsAITranscriberRunning] = useState<boolean>(false);
41
+
42
+ // Converted message list
43
+ const [subtitleInfoList, setSubtitleInfoList] = useState<ITranslationInfo[]>([]);
44
+
45
+ // Helper functions for message conversion
46
+ const parseTranslationTexts = useCallback((translationTexts: any): ITranslationContent[] => {
47
+ if (!translationTexts) {
48
+ return [];
49
+ }
50
+
51
+ // Handle Map type
52
+ if (translationTexts instanceof Map) {
53
+ return Array.from(translationTexts.entries()).map(([language, content]) => ({
54
+ language: String(language),
55
+ text: ensureStringContent(content)
56
+ }));
57
+ }
58
+
59
+ // Handle plain object type
60
+ if (typeof translationTexts === 'object') {
61
+ return Object.entries(translationTexts).map(([language, content]) => ({
62
+ language: String(language),
63
+ text: ensureStringContent(content)
64
+ }));
65
+ }
66
+
67
+ return [];
68
+ }, []);
69
+
70
+ const ensureStringContent = useCallback((content: any): string => {
71
+ if (content === null || content === undefined) {
72
+ return '';
73
+ }
74
+
75
+ if (typeof content === 'string') {
76
+ return content;
77
+ }
78
+
79
+ if (typeof content === 'object') {
80
+ try {
81
+ return JSON.stringify(content);
82
+ } catch (error) {
83
+ console.warn('Failed to stringify translation content:', error);
84
+ return '[Invalid Object]';
85
+ }
86
+ }
87
+
88
+ return String(content);
89
+ }, []);
90
+
91
+ const convertToLegacyFormat = useCallback((messages: TranscriberMessage[]): ITranslationInfo[] => {
92
+ return messages.map(msg => ({
93
+ roundId: msg.segmentId,
94
+ sender: msg.speakerUserId,
95
+ nick: '',
96
+ text: msg.sourceText,
97
+ end: msg.isCompleted,
98
+ translation: parseTranslationTexts(msg.translationTexts)
99
+ }));
100
+ }, [parseTranslationTexts]);
101
+
102
+ // Store watcher callbacks
103
+ const handleAITranscriberEnabledChange = useCallback((value: boolean) => {
104
+ setIsAITranscriberEnabled(value || false);
105
+ }, []);
106
+
107
+ const handleAITranscriberRunningChange = useCallback((value: boolean) => {
108
+ setIsAITranscriberRunning(value || false);
109
+ }, []);
110
+
111
+ const handleRealtimeMessageListChange = useCallback((messageList: TranscriberMessage[]) => {
112
+ if (messageList && messageList.length > 0) {
113
+ const convertedMessages = convertToLegacyFormat(messageList);
114
+ setSubtitleInfoList(convertedMessages);
115
+ } else {
116
+ setSubtitleInfoList([]);
117
+ }
118
+ }, [convertToLegacyFormat]);
119
+
120
+ // Effect for lifecycle management
121
+ useEffect(() => {
122
+ // Try to get initial values from TUIStore if available
123
+ try {
124
+ const initialEnabled = TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED);
125
+ const initialRunning = TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING);
126
+ const initialMessages = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST);
127
+
128
+ if (initialEnabled !== undefined) {
129
+ setIsAITranscriberEnabled(initialEnabled);
130
+ }
131
+ if (initialRunning !== undefined) {
132
+ setIsAITranscriberRunning(initialRunning);
133
+ }
134
+ if (initialMessages) {
135
+ handleRealtimeMessageListChange(initialMessages);
136
+ }
137
+
138
+ // Subscribe to TUIStore changes if the store supports these fields
139
+ if (NAME.IS_AI_TRANSCRIBER_ENABLED && NAME.IS_AI_TRANSCRIBER_RUNNING && NAME.REALTIME_MESSAGE_LIST) {
140
+ TUIStore.watch(StoreName.CALL, {
141
+ [NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange,
142
+ [NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange,
143
+ [NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange,
144
+ });
145
+ }
146
+ } catch (error) {
147
+ console.warn('AI Assistant hook initialization failed:', error);
148
+ // Set default values for development/testing
149
+ setIsAITranscriberEnabled(true);
150
+ setIsAITranscriberRunning(false);
151
+ }
152
+
153
+ // Cleanup function
154
+ return () => {
155
+ try {
156
+ if (NAME.IS_AI_TRANSCRIBER_ENABLED && NAME.IS_AI_TRANSCRIBER_RUNNING && NAME.REALTIME_MESSAGE_LIST) {
157
+ TUIStore.unwatch(StoreName.CALL, {
158
+ [NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange,
159
+ [NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange,
160
+ [NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange,
161
+ });
162
+ }
163
+ } catch (error) {
164
+ console.warn('AI Assistant hook cleanup failed:', error);
165
+ }
166
+ };
167
+ }, [handleAITranscriberEnabledChange, handleAITranscriberRunningChange, handleRealtimeMessageListChange]);
168
+
169
+ return {
170
+ isAITranscriberEnabled,
171
+ isAITranscriberRunning,
172
+ subtitleInfoList,
173
+ };
174
+ }
@@ -1,51 +1,297 @@
1
- // @ts-ignore
2
- import { aiAssistant } from '../../Components/components/common/AIAssistant/index';
3
- import { getRemoteUserProfile } from './utils';
4
-
5
- interface IAIAssistant {
6
- enableAISubtitle: (enable: boolean) => void;
7
- setEngineInstance: (engineInstance: any) => void;
8
- setImInstance: (imInstance: any) => void;
9
- };
10
-
11
- export class AIAssistant implements IAIAssistant {
12
- static instance: IAIAssistant;
13
- private _tuiCallEngine: any = null;
14
- private _imInstance: any = null;
15
-
16
- static getInstance() {
1
+ /**
2
+ * AI Realtime Transcriber State Management
3
+ * @module AITranscriberState
4
+ * @description Manages realtime voice transcription state and operations, including message reception and history
5
+ */
6
+ import { ITUIStore } from '../interface/ITUIStore';
7
+ import { StoreName, NAME } from '../const/index';
8
+ import TuiStore from '../TUIStore/tuiStore';
9
+
10
+ // AI Transcriber events
11
+ export enum RealtimeTranscriberEvent {
12
+ onReceiveTranscriberMessage = 'onReceiveTranscriberMessage',
13
+ onRealtimeTranscriberStarted = 'onRealtimeTranscriberStarted',
14
+ onRealtimeTranscriberStopped = 'onRealtimeTranscriberStopped',
15
+ onRealtimeTranscriberError = 'onRealtimeTranscriberError',
16
+ }
17
+
18
+ // Transcriber language type
19
+ export type TranscriberLanguage = string;
20
+
21
+ // Transcriber message interface
22
+ export interface TranscriberMessage {
23
+ segmentId: string;
24
+ speakerUserId: string;
25
+ sourceText: string;
26
+ translationTexts?: Map<string, string>;
27
+ timestamp: number;
28
+ isCompleted: boolean;
29
+ }
30
+
31
+ // Event callback types
32
+ export interface RealtimeTranscriberEventInfoMap {
33
+ [RealtimeTranscriberEvent.onReceiveTranscriberMessage]: {
34
+ roomId: string | number;
35
+ message: TranscriberMessage;
36
+ };
37
+ [RealtimeTranscriberEvent.onRealtimeTranscriberStarted]: {
38
+ roomId: string | number;
39
+ transcriberRobotId: string;
40
+ sourceLanguage: string;
41
+ };
42
+ [RealtimeTranscriberEvent.onRealtimeTranscriberStopped]: {
43
+ roomId: string | number;
44
+ transcriberRobotId: string;
45
+ };
46
+ [RealtimeTranscriberEvent.onRealtimeTranscriberError]: {
47
+ roomId: string | number;
48
+ transcriberRobotId: string;
49
+ error: number;
50
+ errorMessage: string;
51
+ };
52
+ }
53
+
54
+ export type RealtimeTranscriberEventCallback = <T extends RealtimeTranscriberEvent>(
55
+ eventInfo: RealtimeTranscriberEventInfoMap[T]
56
+ ) => void;
57
+
58
+ const TUIStore: ITUIStore = TuiStore.getInstance();
59
+
60
+ export default class AIAssistant {
61
+ static instance: AIAssistant;
62
+ private _callService: any;
63
+ private eventListeners = new Map<RealtimeTranscriberEvent, RealtimeTranscriberEventCallback[]>();
64
+
65
+ constructor(options: { callService: any }) {
66
+ this._callService = options.callService;
67
+ }
68
+
69
+ static getInstance(options: { callService: any }) {
17
70
  if (!AIAssistant.instance) {
18
- AIAssistant.instance = new AIAssistant();
71
+ AIAssistant.instance = new AIAssistant(options);
19
72
  }
20
73
  return AIAssistant.instance;
21
74
  }
22
75
 
23
- public enableAISubtitle(enable: boolean): void {
24
- this._tuiCallEngine?.reportLog?.({
25
- name: 'TUICallKit.enableAISubtitle.start',
26
- data: { enable },
27
- });
76
+ /**
77
+ * Get AI Transcriber Manager instance
78
+ * @returns AITranscriberManager instance or null if not available
79
+ */
80
+ private getTranscriberManager = () => {
81
+ const callEngine = this._callService?.getTUICallEngineInstance();
82
+ if (!callEngine) {
83
+ console.warn(`${NAME.PREFIX}Failed to get call engine instance.`);
84
+ return null;
85
+ }
28
86
 
29
- const trtcCloudInstance = this._tuiCallEngine.getTRTCCloudInstance();
30
- const getNickName = async(userId: string) => {
31
- const res = await getRemoteUserProfile([userId], this._imInstance);
32
- return res.length ? res[0] : [];
87
+ const trtcCloud = callEngine.getTRTCCloudInstance?.();
88
+ if (!trtcCloud || !trtcCloud.getAITranscriberManager) {
89
+ console.warn(`${NAME.PREFIX}AI Transcriber not supported.`);
90
+ return null;
33
91
  }
34
- if (enable) {
35
- aiAssistant.initASR({
36
- trtcCloudInstance,
37
- // @ts-ignore
38
- getNickName,
92
+
93
+ return trtcCloud.getAITranscriberManager();
94
+ };
95
+
96
+ private triggerEvent = <T extends RealtimeTranscriberEvent>(
97
+ event: T,
98
+ eventInfo: RealtimeTranscriberEventInfoMap[T],
99
+ ): void => {
100
+ const listeners = this.eventListeners.get(event);
101
+ if (listeners) {
102
+ listeners.forEach((callback) => {
103
+ try {
104
+ callback(eventInfo);
105
+ } catch (error) {
106
+ console.error(`${NAME.PREFIX}Error in event callback for ${event}:`, error);
107
+ }
39
108
  });
109
+ }
110
+ };
111
+
112
+ public startRealtimeTranscriber = async (config: {
113
+ sourceLanguage: TranscriberLanguage;
114
+ translationLanguages?: TranscriberLanguage[];
115
+ }) => {
116
+ const transcriberManager = this.getTranscriberManager();
117
+ if (!transcriberManager) {
118
+ return null;
119
+ }
120
+
121
+ try {
122
+ const transcriberRobotId = await transcriberManager.startRealtimeTranscriber(config);
123
+
124
+ TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, transcriberRobotId);
125
+ TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING, true);
126
+ return transcriberRobotId;
127
+ } catch (error) {
128
+ console.error(`${NAME.PREFIX}startRealtimeTranscriber error:`, error);
129
+ throw error;
130
+ }
131
+ };
132
+
133
+ public stopRealtimeTranscriber = async () => {
134
+ const transcriberManager = this.getTranscriberManager();
135
+ if (!transcriberManager) {
136
+ return;
137
+ }
138
+
139
+ try {
140
+ const transcriberRobotId = TUIStore.getData(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID);
141
+
142
+ if (transcriberRobotId) {
143
+ await transcriberManager.stopRealtimeTranscriber(transcriberRobotId);
144
+ }
145
+
146
+ TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, null);
147
+ TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING, false);
148
+ } catch (error) {
149
+ console.error(`${NAME.PREFIX}stopRealtimeTranscriber error:`, error);
150
+ throw error;
151
+ }
152
+ };
153
+
154
+ public updateRealTimeTranscriber = async (config: {
155
+ sourceLanguage: TranscriberLanguage;
156
+ translationLanguages?: TranscriberLanguage[];
157
+ }) => {
158
+ const transcriberManager = this.getTranscriberManager();
159
+ if (!transcriberManager) {
160
+ return null;
161
+ }
162
+
163
+ try {
164
+ const currentTranscriberRobotId = TUIStore.getData(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID);
165
+
166
+ if (currentTranscriberRobotId) {
167
+ await transcriberManager.stopRealtimeTranscriber(currentTranscriberRobotId);
168
+ }
169
+
170
+ const newTranscriberRobotId = await transcriberManager.startRealtimeTranscriber(config);
171
+ TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, newTranscriberRobotId);
172
+
173
+ return newTranscriberRobotId;
174
+ } catch (error) {
175
+ console.error(`${NAME.PREFIX}updateRealTimeTranscriber error:`, error);
176
+ throw error;
177
+ }
178
+ };
179
+
180
+ public subscribeEvent = (event: RealtimeTranscriberEvent, callback: RealtimeTranscriberEventCallback) => {
181
+ if (!this.eventListeners.has(event)) {
182
+ this.eventListeners.set(event, []);
183
+ }
184
+ this.eventListeners.get(event)?.push(callback);
185
+ };
186
+
187
+ public unsubscribeEvent = (event: RealtimeTranscriberEvent, callback: RealtimeTranscriberEventCallback) => {
188
+ const listeners = this.eventListeners.get(event);
189
+ if (listeners) {
190
+ const index = listeners.indexOf(callback);
191
+ if (index > -1) {
192
+ listeners.splice(index, 1);
193
+ }
194
+ if (listeners.length === 0) {
195
+ this.eventListeners.delete(event);
196
+ }
197
+ }
198
+ };
199
+
200
+ private onReceiveTranscriberMessage = (
201
+ _roomId: string | number,
202
+ message: {
203
+ segmentId: string;
204
+ speakerUserId: string;
205
+ sourceText: string;
206
+ translationTexts?: Map<string, string>;
207
+ timestamp: number;
208
+ isCompleted: boolean;
209
+ },
210
+ ) => {
211
+ const { segmentId } = message;
212
+ let realtimeMessageList: TranscriberMessage[] = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST) || [];
213
+
214
+ const idx = realtimeMessageList.findIndex(msg => msg.segmentId === segmentId);
215
+ if (idx >= 0) {
216
+ realtimeMessageList[idx] = message;
40
217
  } else {
41
- aiAssistant.destroyASR();
218
+ realtimeMessageList.push(message);
42
219
  }
43
- }
220
+
221
+ TUIStore.update(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST, [...realtimeMessageList]);
222
+
223
+ this.triggerEvent(RealtimeTranscriberEvent.onReceiveTranscriberMessage, {
224
+ roomId: _roomId,
225
+ message,
226
+ });
227
+ };
44
228
 
45
- public setEngineInstance(engineInstance: any): void {
46
- this._tuiCallEngine = engineInstance;
47
- }
48
- public setImInstance(imInstance: any): void {
49
- this._imInstance = imInstance;
50
- }
229
+ private onRealtimeTranscriberStarted = (_roomId: string | number, _transcriberRobotId: string, _sourceLanguage: string) => {
230
+ this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberStarted, {
231
+ roomId: _roomId,
232
+ transcriberRobotId: _transcriberRobotId,
233
+ sourceLanguage: _sourceLanguage,
234
+ });
235
+ };
236
+
237
+ private onRealtimeTranscriberStopped = (_roomId: string | number, _transcriberRobotId: string) => {
238
+ this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberStopped, {
239
+ roomId: _roomId,
240
+ transcriberRobotId: _transcriberRobotId,
241
+ });
242
+ };
243
+
244
+ private onRealtimeTranscriberError = (_roomId: string | number, _transcriberRobotId: string, error: number, errorMessage: string) => {
245
+ this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberError, {
246
+ roomId: _roomId,
247
+ transcriberRobotId: _transcriberRobotId,
248
+ error,
249
+ errorMessage,
250
+ });
251
+ };
252
+
253
+ public bindEvent = () => {
254
+ const transcriberManager = this.getTranscriberManager();
255
+ if (!transcriberManager) {
256
+ return;
257
+ }
258
+
259
+ try {
260
+ transcriberManager.addListener({
261
+ onReceiveTranscriberMessage: this.onReceiveTranscriberMessage,
262
+ onRealtimeTranscriberStarted: this.onRealtimeTranscriberStarted,
263
+ onRealtimeTranscriberStopped: this.onRealtimeTranscriberStopped,
264
+ onRealtimeTranscriberError: this.onRealtimeTranscriberError,
265
+ });
266
+ } catch (error) {
267
+ console.error(`${NAME.PREFIX}bindEvent error:`, error);
268
+ }
269
+ };
270
+
271
+ public unbindEvent = () => {
272
+ const transcriberManager = this.getTranscriberManager();
273
+ if (!transcriberManager) {
274
+ return;
275
+ }
276
+
277
+ try {
278
+ transcriberManager.removeListener({
279
+ onReceiveTranscriberMessage: this.onReceiveTranscriberMessage,
280
+ onRealtimeTranscriberStarted: this.onRealtimeTranscriberStarted,
281
+ onRealtimeTranscriberStopped: this.onRealtimeTranscriberStopped,
282
+ onRealtimeTranscriberError: this.onRealtimeTranscriberError,
283
+ });
284
+ } catch (error) {
285
+ console.error(`${NAME.PREFIX}unbindEvent error:`, error);
286
+ }
287
+ };
288
+
289
+ public reset = () => {
290
+ TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, null);
291
+ TUIStore.update(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST, []);
292
+ TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED, false);
293
+ this.eventListeners.clear();
294
+ };
51
295
  }
296
+
297
+
@@ -27,15 +27,15 @@ const TUIGlobal: ITUIGlobal = TuiGlobal.getInstance();
27
27
  const TUIStore: ITUIStore = TuiStore.getInstance();
28
28
  const uiDesign = UIDesign.getInstance();
29
29
  uiDesign.setTUIStore(TUIStore);
30
- const version = '4.4.3';
31
- import { AIAssistant } from './AIAssistant'; // 仅 web 支持 AI 实时字幕
32
- const aiAssistant = AIAssistant.getInstance();
30
+ const version = '4.4.4';
31
+ import AIAssistant from './AIAssistant'; // 仅 web 支持 AI 实时字幕
33
32
  const frameWork = 'react';
34
33
  export { TUIGlobal, TUIStore, uiDesign };
35
34
 
36
35
  export default class TUICallService {
37
36
  static instance: TUICallService;
38
37
  public _tuiCallEngine: any;
38
+ public _aiAssistant: any = null;
39
39
  private _tim: any = null;
40
40
  private _TUICore: any = null;
41
41
  private _timerId: number = -1;
@@ -62,6 +62,7 @@ export default class TUICallService {
62
62
  this._engineEventHandler = EngineEventHandler.getInstance({ callService: this });
63
63
 
64
64
  this._chatCombine = ChatCombine.getInstance({ callService: this });
65
+ this._aiAssistant = AIAssistant.getInstance({ callService: this });
65
66
  initBrowserCloseDetection(this.handleExceptionExit.bind(this));
66
67
  }
67
68
  static getInstance() {
@@ -122,9 +123,7 @@ export default class TUICallService {
122
123
  TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { userId: userID });
123
124
  TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { userId: userID });
124
125
  uiDesign.updateViewBackgroundUserId('local');
125
- aiAssistant.setEngineInstance(this._tuiCallEngine);
126
- aiAssistant.setImInstance(this.getTim());
127
- this.enableAISubtitle(true);
126
+ this._aiAssistant?.bindEvent();
128
127
 
129
128
  await this._tuiCallEngine.login({ userID, userSig, assetsPath: '' }); // web && mini
130
129
  const uiConfig = TUIStore.getData(StoreName.CALL, NAME.CUSTOM_UI_CONFIG);
@@ -157,6 +156,8 @@ export default class TUICallService {
157
156
  await this._tuiCallEngine.destroyInstance();
158
157
  this._tuiCallEngine = null;
159
158
  }
159
+ this._aiAssistant?.unbindEvent();
160
+ this._aiAssistant?.reset();
160
161
  this._bellContext?.destroy();
161
162
  this._bellContext = null;
162
163
  } catch (error) {
@@ -275,6 +276,16 @@ export default class TUICallService {
275
276
  throw error;
276
277
  }
277
278
  }
279
+ public async enableAITranscriber(enable: boolean) {
280
+ try {
281
+ // Update store to control UI translation switch and translation area visibility
282
+ TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED, enable);
283
+ console.log(`${NAME.PREFIX}enableAITranscriber: ${enable}.`);
284
+ } catch (error: any) {
285
+ console.error(`${NAME.PREFIX}enableAITranscriber failed, error: ${error}.`);
286
+ throw error;
287
+ }
288
+ }
278
289
  // 修改默认铃声:只支持本地铃声文件,不支持在线铃声文件;修改铃声修改的是被叫的铃声
279
290
  public async setCallingBell(filePath?: string) {
280
291
  let isCheckFileExist: boolean = true;
@@ -310,9 +321,7 @@ export default class TUICallService {
310
321
  public setCameraDefaultState(isOpen: boolean) {
311
322
  uiDesign.setCameraDefaultState(isOpen);
312
323
  }
313
- public enableAISubtitle(enable: boolean): void {
314
- aiAssistant.enableAISubtitle(enable);
315
- }
324
+ // AI Subtitle functionality removed, focusing on real-time transcriber
316
325
  // =============================【实验性接口】=============================
317
326
  public callExperimentalAPI(jsonStr: string) {
318
327
  const jsonObj = JSON.parse(jsonStr);
@@ -744,6 +753,7 @@ export default class TUICallService {
744
753
  case NAME.ENABLE_FLOAT_WINDOW:
745
754
  case NAME.LOCAL_USER_INFO:
746
755
  case NAME.IS_SHOW_ENABLE_VIRTUAL_BACKGROUND:
756
+ case NAME.IS_AI_TRANSCRIBER_ENABLED:
747
757
  case NAME.IS_FORCE_USE_V2_API:
748
758
  case NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN: {
749
759
  return false;
@@ -54,6 +54,11 @@ export default class CallStore {
54
54
  // translate function
55
55
  translate: t,
56
56
  isForceUseV2API: false,
57
+ // AI Transcriber related fields
58
+ transcriberRobotId: null,
59
+ realtimeMessageList: [],
60
+ isAITranscriberEnabled: false,
61
+ isAITranscriberRunning: false,
57
62
  };
58
63
  public store: ICallStore = deepClone(this.defaultStore);
59
64
  public prevStore: ICallStore = deepClone(this.defaultStore);
@@ -39,6 +39,10 @@ export const CALL_DATA_KEY: any = {
39
39
  PUSHER_ID: 'pusherId',
40
40
  IS_FORCE_USE_V2_API: 'isForceUseV2API',
41
41
  MODAL_ERROR: 'modalError',
42
+ TRANSCRIBER_ROBOT_ID: 'transcriberRobotId',
43
+ REALTIME_MESSAGE_LIST: 'realtimeMessageList',
44
+ IS_AI_TRANSCRIBER_ENABLED: 'isAITranscriberEnabled', // Controls UI visibility
45
+ IS_AI_TRANSCRIBER_RUNNING: 'isAITranscriberRunning', // Controls AI transcriber running state
42
46
  };
43
47
 
44
48
  export const CHAT_DATA_KEY: any = {
@@ -52,4 +52,9 @@ export interface ICallStore {
52
52
  // translate function
53
53
  translate: Function,
54
54
  isForceUseV2API: boolean, // 是否使用 call/groupCall 接口, 默认: false
55
+ // AI Transcriber related fields
56
+ transcriberRobotId: string | null, // AI 转写机器人 ID
57
+ realtimeMessageList: [], // 实时转写消息列表
58
+ isAITranscriberEnabled: boolean, // AI 转写UI是否显示
59
+ isAITranscriberRunning: boolean, // AI transcriber running state
55
60
  }
@@ -146,4 +146,19 @@ export const en = {
146
146
  'error.10013': "Call failed: You have been blocked by the other party or you have blocked the other party",
147
147
  'error.10014': "Call initiation failed. User ID is invalid. Please confirm that the user is registered.",
148
148
  'error': 'Error',
149
+ // AI Subtitle Settings
150
+ 'ai-subtitle': 'AI Subtitle',
151
+ 'ai-subtitle-settings': 'AI Subtitle Settings',
152
+ 'recognition-language': 'Recognition Language',
153
+ 'translation-language': 'Translation Language',
154
+ 'subtitle-display-settings': 'Subtitle Display Settings',
155
+ 'subtitle-display-bilingual': 'Show Bilingual Subtitles',
156
+ 'no-translation': 'No Translation',
157
+ 'show-bilingual': 'Show Bilingual',
158
+ 'show-original-only': 'Show Original Only',
159
+ 'save': 'Save',
160
+ 'select-recognition-language': 'Please select recognition language',
161
+ 'select-translation-language': 'Please select translation language',
162
+ 'confirm': 'Confirm',
163
+ 'recognition-and-translation-settings': 'Recognition & Translation Settings',
149
164
  };
@@ -145,4 +145,19 @@ export const ja_JP = {
145
145
  'error.10013': "通話失敗:相手にブロックされているか、または相手をブロックしています",
146
146
  'error.10014': "通話の開始に失敗しました。ユーザーIDが無効です。ユーザーが登録されていることを確認してください。",
147
147
  'error': 'エラー',
148
+ // AI Subtitle Settings
149
+ 'ai-subtitle': 'AI字幕',
150
+ 'ai-subtitle-settings': 'AI字幕設定',
151
+ 'recognition-language': '認識言語',
152
+ 'translation-language': '翻訳言語',
153
+ 'subtitle-display-settings': '字幕表示設定',
154
+ 'subtitle-display-bilingual': 'バイリンガル字幕を表示',
155
+ 'no-translation': '翻訳しない',
156
+ 'show-bilingual': 'バイリンガル表示',
157
+ 'show-original-only': '原文のみ表示',
158
+ 'save': '保存',
159
+ 'select-recognition-language': '認識言語を選択してください',
160
+ 'select-translation-language': '翻訳言語を選択してください',
161
+ 'confirm': '確認',
162
+ 'recognition-and-translation-settings': '認識と翻訳の設定',
148
163
  };