@trtc/calls-uikit-vue 4.4.2 → 4.4.5

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 (57) hide show
  1. package/package.json +2 -2
  2. package/src/Components/TUICallKit.vue +1 -1
  3. package/src/Components/assets/aiAssistant/desktop/subtitleSettings.svg +4 -0
  4. package/src/Components/assets/aiAssistant/mobile/close-aiAssistant.svg +7 -0
  5. package/src/Components/assets/aiAssistant/mobile/open-aiAssistant.svg +7 -0
  6. package/src/Components/assets/aiAssistant/mobile/subtitleSettings.svg +3 -0
  7. package/src/Components/components/base/CustomSelect/CustomSelect.ts +45 -0
  8. package/src/Components/components/base/CustomSelect/CustomSelect.vue +199 -0
  9. package/src/Components/components/common/AIAssistant/AISubtitle.vue +155 -37
  10. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.vue +35 -0
  11. package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.vue +89 -0
  12. package/src/Components/components/common/AIAssistant/components/SubtitleContent.vue +234 -0
  13. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.vue +534 -0
  14. package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.vue +294 -0
  15. package/src/Components/components/common/TopBar/TopBar.vue +41 -15
  16. package/src/Components/hooks/index.ts +1 -0
  17. package/src/Components/hooks/useAIAssistant.ts +142 -0
  18. package/src/Components/hooks/useGetVolumeMap.ts +2 -2
  19. package/src/TUICallService/CallService/AIAssistant.ts +285 -39
  20. package/src/TUICallService/CallService/UIKitModal.ts +14 -6
  21. package/src/TUICallService/CallService/bellContext.ts +25 -2
  22. package/src/TUICallService/CallService/engineEventHandler.ts +6 -1
  23. package/src/TUICallService/CallService/index.ts +72 -39
  24. package/src/TUICallService/CallService/miniProgram.ts +0 -12
  25. package/src/TUICallService/TUIStore/callStore.ts +6 -1
  26. package/src/TUICallService/UIKitModal/UIKitModal.ts +117 -0
  27. package/src/TUICallService/UIKitModal/index.ts +2 -0
  28. package/src/TUICallService/UIKitModal/type.ts +15 -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/TUICallService/utils/common-utils.ts +1 -1
  35. package/src/index.ts +1 -1
  36. package/stats.html +1 -1
  37. package/tuicall-uikit-vue.es.js +4199 -3474
  38. package/tuicall-uikit-vue.umd.js +2 -2
  39. package/types/Components/components/base/CustomSelect/CustomSelect.d.ts +41 -0
  40. package/types/Components/hooks/index.d.ts +1 -0
  41. package/types/Components/hooks/useAIAssistant.d.ts +19 -0
  42. package/types/TUICallService/CallService/AIAssistant.d.ts +72 -15
  43. package/types/TUICallService/CallService/bellContext.d.ts +3 -0
  44. package/types/TUICallService/CallService/index.d.ts +4 -3
  45. package/types/TUICallService/CallService/miniProgram.d.ts +0 -1
  46. package/types/TUICallService/UIKitModal/UIKitModal.d.ts +4 -0
  47. package/types/TUICallService/UIKitModal/index.d.ts +2 -0
  48. package/types/TUICallService/UIKitModal/type.d.ts +13 -0
  49. package/types/TUICallService/interface/ICallStore.d.ts +4 -0
  50. package/types/TUICallService/locales/en.d.ts +14 -0
  51. package/types/TUICallService/locales/ja_JP.d.ts +14 -0
  52. package/types/TUICallService/locales/zh-cn.d.ts +14 -0
  53. package/types/tsconfig.tsbuildinfo +1 -1
  54. package/src/Components/components/common/AIAssistant/AIAssistant.ts +0 -130
  55. package/src/Components/components/common/AIAssistant/index.ts +0 -11
  56. package/types/Components/components/common/AIAssistant/AIAssistant.d.ts +0 -40
  57. package/types/Components/components/common/AIAssistant/index.d.ts +0 -4
@@ -0,0 +1,534 @@
1
+ <template>
2
+ <div v-if="visible" class="subtitle-settings-overlay">
3
+ <div class="subtitle-settings-modal" @click.stop>
4
+ <!-- Header -->
5
+ <div class="modal-header">
6
+ <h3 class="modal-title">{{ t('ai-subtitle-settings') }}</h3>
7
+ </div>
8
+
9
+ <!-- Content -->
10
+ <div class="modal-content">
11
+ <!-- Recognition and Translation Settings Group -->
12
+ <div class="setting-group">
13
+ <div class="group-title">{{ t('recognition-and-translation-settings') }}</div>
14
+ <div class="group-content">
15
+ <!-- Recognition Language -->
16
+ <div class="setting-section">
17
+ <div class="section-header" @click="showRecognitionLanguageSelector = true">
18
+ <span class="section-title">{{ t('recognition-language') }}</span>
19
+ <span class="section-value">
20
+ {{ getLanguageDisplayName(recognitionLanguage) }}
21
+ <svg class="arrow-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
22
+ <path d="M6 4L10 8L6 12" stroke="#999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
23
+ </svg>
24
+ </span>
25
+ </div>
26
+ </div>
27
+
28
+ <!-- Translation Language -->
29
+ <div class="setting-section">
30
+ <div class="section-header" @click="showTranslationLanguageSelector = true">
31
+ <span class="section-title">{{ t('translation-language') }}</span>
32
+ <span class="section-value">
33
+ {{ getLanguageDisplayName(translationLanguage) || t('no-translation') }}
34
+ <svg class="arrow-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
35
+ <path d="M6 4L10 8L6 12" stroke="#999" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
36
+ </svg>
37
+ </span>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Show Bilingual Subtitles -->
42
+ <div class="setting-section">
43
+ <div class="section-header">
44
+ <span class="section-title">{{ t('subtitle-display-bilingual') }}</span>
45
+ <div class="toggle-switch" :class="{ active: subtitleEnabled }" @click="toggleSubtitle">
46
+ <div class="toggle-slider"></div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <!-- Footer with buttons -->
55
+ <div class="modal-footer">
56
+ <button class="btn btn-cancel" @click="closeModal">{{ t('Cancel') }}</button>
57
+ <button class="btn btn-confirm" @click="confirmSettings">{{ t('confirm') }}</button>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Language Selector Modals -->
62
+ <!-- Recognition Language Selector -->
63
+ <div v-if="showRecognitionLanguageSelector" class="language-selector-overlay" @click="showRecognitionLanguageSelector = false">
64
+ <div class="language-selector" @click.stop>
65
+ <div class="selector-header">
66
+ <h3 class="selector-title">{{ t('select-recognition-language') }}</h3>
67
+ <div class="header-placeholder"></div>
68
+ </div>
69
+ <div class="language-list">
70
+ <div
71
+ v-for="lang in (Array.isArray(recognitionLanguageOptions) ? recognitionLanguageOptions : [])"
72
+ :key="lang.value"
73
+ class="language-item"
74
+ :class="{ selected: recognitionLanguage === lang.value }"
75
+ @click="selectRecognitionLanguage(lang.value)"
76
+ >
77
+ <span>{{ lang.label }}</span>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <!-- Translation Language Selector -->
84
+ <div v-if="showTranslationLanguageSelector" class="language-selector-overlay" @click="showTranslationLanguageSelector = false">
85
+ <div class="language-selector" @click.stop>
86
+ <div class="selector-header">
87
+ <h3 class="selector-title">{{ t('select-translation-language') }}</h3>
88
+ <div class="header-placeholder"></div>
89
+ </div>
90
+ <div class="language-list">
91
+ <div
92
+ v-for="lang in (Array.isArray(translationLanguageOptions) ? translationLanguageOptions : [])"
93
+ :key="lang.value"
94
+ class="language-item"
95
+ :class="{ selected: translationLanguage === lang.value }"
96
+ @click="selectTranslationLanguage(lang.value)"
97
+ >
98
+ <span>{{ lang.label }}</span>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </template>
105
+
106
+ <script lang="ts" setup>
107
+ import { ref, computed, watch } from '../../../../../adapter-vue';
108
+ import { TUICallKitAPI } from '../../../../../TUICallService/index';
109
+ import { useTranslate } from '../../../../hooks/useTranslate';
110
+
111
+ interface Props {
112
+ visible: boolean;
113
+ }
114
+
115
+ interface Emits {
116
+ (e: 'update:visible', value: boolean): void;
117
+ (e: 'confirm', settings: {
118
+ recognitionLanguage: string;
119
+ translationLanguage: string;
120
+ subtitleEnabled: boolean;
121
+ }): void;
122
+ }
123
+
124
+ const props = withDefaults(defineProps<Props>(), {
125
+ visible: false
126
+ });
127
+ const emit = defineEmits<Emits>();
128
+
129
+ // Use i18n translation
130
+ const t = useTranslate();
131
+
132
+ // Confirmed settings (saved on confirm button click)
133
+ const savedSettings = ref({
134
+ recognitionLanguage: 'zh',
135
+ translationLanguage: 'en',
136
+ subtitleEnabled: true
137
+ });
138
+
139
+ // Temporary editing state (current user selections)
140
+ const recognitionLanguage = ref('zh');
141
+ const translationLanguage = ref('en');
142
+ const subtitleEnabled = ref(true);
143
+
144
+ // UI state
145
+ const showRecognitionLanguageSelector = ref(false);
146
+ const showTranslationLanguageSelector = ref(false);
147
+
148
+ // Language options - supported recognition languages
149
+ // Use native language names for better UX (users can always recognize their own language)
150
+ const recognitionLanguageOptions = computed(() => [
151
+ { value: 'zh', label: '中文' },
152
+ { value: 'en', label: 'English' },
153
+ ]);
154
+
155
+ // Language options - translation target languages (base list)
156
+ // Use native language names for better UX, but keep "no translation" internationalized
157
+ const baseTranslationLanguageOptions = computed(() => [
158
+ { value: '', label: t.value('no-translation') },
159
+ { value: 'zh', label: 'Chinese (中文)' },
160
+ { value: 'en', label: 'English (英语)' },
161
+ { value: 'vi', label: 'Vietnamese (越南语)' },
162
+ { value: 'ja', label: 'Japanese (日语)' },
163
+ { value: 'ko', label: 'Korean (韩语)' },
164
+ { value: 'id', label: 'Indonesian (印尼语)' },
165
+ { value: 'th', label: 'Thai (泰语)' },
166
+ { value: 'pt', label: 'Portuguese (葡萄牙语)' },
167
+ { value: 'ar', label: 'Arabic (阿拉伯语)' },
168
+ { value: 'es', label: 'Spanish (西班牙语)' },
169
+ { value: 'fr', label: 'French (法语)' },
170
+ { value: 'ms', label: 'Malay (马来语)' },
171
+ { value: 'de', label: 'German (德语)' },
172
+ { value: 'it', label: 'Italian (意大利语)' },
173
+ { value: 'ru', label: 'Russian (俄语)' },
174
+ ]);
175
+
176
+ // Computed property to filter translation options based on selected recognition language
177
+ const translationLanguageOptions = computed(() => {
178
+ return baseTranslationLanguageOptions.value.filter(option =>
179
+ option.value === '' || option.value !== recognitionLanguage.value
180
+ );
181
+ });
182
+
183
+ const getLanguageDisplayName = (value: string) => {
184
+ const allOptions = [
185
+ ...(Array.isArray(recognitionLanguageOptions.value) ? recognitionLanguageOptions.value : []),
186
+ ...(Array.isArray(translationLanguageOptions.value) ? translationLanguageOptions.value : [])
187
+ ];
188
+ const option = allOptions.find(opt => opt.value === value);
189
+ return option?.label || '';
190
+ };
191
+
192
+ const closeModal = () => {
193
+ // Restore saved settings when closing without confirming
194
+ recognitionLanguage.value = savedSettings.value.recognitionLanguage;
195
+ translationLanguage.value = savedSettings.value.translationLanguage;
196
+ subtitleEnabled.value = savedSettings.value.subtitleEnabled;
197
+ showRecognitionLanguageSelector.value = false;
198
+ showTranslationLanguageSelector.value = false;
199
+
200
+ emit('update:visible', false);
201
+ };
202
+
203
+ const confirmSettings = async () => {
204
+ try {
205
+ // Save current settings as confirmed settings
206
+ savedSettings.value = {
207
+ recognitionLanguage: recognitionLanguage.value,
208
+ translationLanguage: translationLanguage.value,
209
+ subtitleEnabled: subtitleEnabled.value
210
+ };
211
+
212
+ // Call updateRealTimeTranscriber when settings are confirmed
213
+ const translationLanguages = translationLanguage.value ? [translationLanguage.value] : [];
214
+
215
+ await TUICallKitAPI._aiAssistant.updateRealTimeTranscriber({
216
+ sourceLanguage: recognitionLanguage.value,
217
+ translationLanguages: translationLanguages,
218
+ });
219
+
220
+ // Emit settings when confirming
221
+ emit('confirm', {
222
+ recognitionLanguage: recognitionLanguage.value,
223
+ translationLanguage: translationLanguage.value,
224
+ subtitleEnabled: subtitleEnabled.value,
225
+ });
226
+ } catch (error) {
227
+ console.error('Failed to update AI transcriber settings:', error);
228
+ }
229
+
230
+ emit('update:visible', false);
231
+ };
232
+
233
+ const selectRecognitionLanguage = (value: string) => {
234
+ recognitionLanguage.value = value;
235
+ showRecognitionLanguageSelector.value = false;
236
+ };
237
+
238
+ const selectTranslationLanguage = (value: string) => {
239
+ translationLanguage.value = value;
240
+ showTranslationLanguageSelector.value = false;
241
+ };
242
+
243
+ const toggleSubtitle = () => {
244
+ subtitleEnabled.value = !subtitleEnabled.value;
245
+ };
246
+
247
+ // Watch for recognition language changes and reset translation language if it conflicts
248
+ watch(recognitionLanguage, (newRecognitionLang) => {
249
+ // If the current translation language is the same as the new recognition language, reset it
250
+ if (translationLanguage.value === newRecognitionLang) {
251
+ translationLanguage.value = '';
252
+ }
253
+ });
254
+ </script>
255
+
256
+ <style lang="scss" scoped>
257
+ .subtitle-settings-overlay {
258
+ position: fixed;
259
+ top: 0;
260
+ left: 0;
261
+ right: 0;
262
+ bottom: 0;
263
+ width: 100vw;
264
+ height: 100vh;
265
+ background-color: rgba(0, 0, 0, 0.5);
266
+ z-index: 1000;
267
+ display: flex;
268
+ align-items: center;
269
+ justify-content: center;
270
+ padding: 20px;
271
+ font-family: PingFang SC;
272
+ }
273
+
274
+ .subtitle-settings-modal {
275
+ background-color: white;
276
+ border-radius: 12px;
277
+ width: 90%;
278
+ max-width: 400px;
279
+ max-height: 80vh;
280
+ display: flex;
281
+ flex-direction: column;
282
+ overflow: hidden;
283
+ animation: modalSlideIn 0.3s ease-out;
284
+ }
285
+
286
+ @keyframes modalSlideIn {
287
+ from {
288
+ opacity: 0;
289
+ transform: scale(0.9) translateY(-20px);
290
+ }
291
+ to {
292
+ opacity: 1;
293
+ transform: scale(1) translateY(0);
294
+ }
295
+ }
296
+
297
+ .modal-header {
298
+ text-align: center;
299
+ padding: 20px 20px 16px;
300
+ }
301
+
302
+ .modal-title {
303
+ font-size: 18px;
304
+ font-weight: 500;
305
+ color: #333;
306
+ margin: 0;
307
+ }
308
+
309
+ .modal-content {
310
+ flex: 1;
311
+ overflow-y: auto;
312
+ padding: 16px 0;
313
+ }
314
+
315
+ .modal-footer {
316
+ display: flex;
317
+ gap: 12px;
318
+ padding: 16px 20px 20px;
319
+ }
320
+
321
+ .btn {
322
+ flex: 1;
323
+ width: 200px;
324
+ height: 44px;
325
+ border-radius: 20px;
326
+ border: none;
327
+ font-size: 16px;
328
+ font-weight: 500;
329
+ cursor: pointer;
330
+ transition: all 0.2s;
331
+
332
+ &.btn-cancel {
333
+ color: #1C66E5;
334
+ border: 1px solid #1C66E5;
335
+ background-color: white;
336
+ }
337
+
338
+ &.btn-confirm {
339
+ background-color: #1677ff;
340
+ color: white;
341
+ }
342
+ }
343
+
344
+ .setting-group {
345
+ margin-bottom: 12px;
346
+
347
+ &:last-child {
348
+ margin-bottom: 0;
349
+ }
350
+ }
351
+
352
+ .group-title {
353
+ font-size: 13px;
354
+ color: #666;
355
+ padding: 0 20px 8px;
356
+ margin-bottom: 2px;
357
+ }
358
+
359
+ .group-content {
360
+ background: white;
361
+ border-radius: 8px;
362
+ margin: 0 16px;
363
+ overflow: hidden;
364
+ border: 1px solid #f0f0f0;
365
+ }
366
+
367
+ .setting-section {
368
+ border-bottom: 1px solid #f0f0f0;
369
+
370
+ &:last-child {
371
+ border-bottom: none;
372
+ }
373
+ }
374
+
375
+ .section-header {
376
+ display: flex;
377
+ align-items: center;
378
+ justify-content: space-between;
379
+ padding: 16px;
380
+ cursor: pointer;
381
+ transition: background-color 0.2s;
382
+
383
+ &:active {
384
+ background-color: #f8f8f8;
385
+ }
386
+ }
387
+
388
+ .section-title {
389
+ font-size: 16px;
390
+ color: #333;
391
+ font-weight: 400;
392
+ }
393
+
394
+ .section-value {
395
+ display: flex;
396
+ align-items: center;
397
+ font-size: 14px;
398
+ color: #666;
399
+ gap: 4px;
400
+ }
401
+
402
+ .arrow-icon {
403
+ flex-shrink: 0;
404
+ }
405
+
406
+ .toggle-switch {
407
+ width: 44px;
408
+ height: 24px;
409
+ background-color: #ccc;
410
+ border-radius: 12px;
411
+ position: relative;
412
+ cursor: pointer;
413
+ transition: background-color 0.3s;
414
+
415
+ &.active {
416
+ background-color: #1677ff;
417
+ }
418
+ }
419
+
420
+ .toggle-slider {
421
+ width: 20px;
422
+ height: 20px;
423
+ background-color: white;
424
+ border-radius: 50%;
425
+ position: absolute;
426
+ top: 2px;
427
+ left: 2px;
428
+ transition: transform 0.3s;
429
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
430
+
431
+ .toggle-switch.active & {
432
+ transform: translateX(20px);
433
+ }
434
+ }
435
+
436
+ // Language Selector Styles
437
+ .language-selector-overlay {
438
+ position: fixed;
439
+ top: 0;
440
+ left: 0;
441
+ right: 0;
442
+ bottom: 0;
443
+ width: 100%;
444
+ height: 100vh;
445
+ background-color: rgba(0, 0, 0, 0.5);
446
+ z-index: 1100;
447
+ display: flex;
448
+ flex-direction: column;
449
+ justify-content: flex-end;
450
+ overflow: hidden;
451
+ }
452
+
453
+ .language-selector {
454
+ max-height: 70vh;
455
+ display: flex;
456
+ flex-direction: column;
457
+ background-color: white;
458
+ border-radius: 16px 16px 0 0;
459
+ padding-bottom: env(safe-area-inset-bottom);
460
+ transform: translateY(0);
461
+ animation: slideUp 0.3s ease-out;
462
+ }
463
+
464
+ @keyframes slideUp {
465
+ from {
466
+ transform: translateY(100%);
467
+ }
468
+ to {
469
+ transform: translateY(0);
470
+ }
471
+ }
472
+
473
+ .selector-header {
474
+ text-align: center;
475
+ padding: 16px 16px 12px;
476
+ border-bottom: 1px solid #e5e5e5;
477
+ background: white;
478
+ border-radius: 16px 16px 0 0;
479
+ }
480
+
481
+ .selector-title {
482
+ font-size: 12px;
483
+ font-weight: 400;
484
+ color: #00000066;
485
+ margin: 0;
486
+ }
487
+
488
+ .language-list {
489
+ flex: 1;
490
+ overflow-y: auto;
491
+ padding: 0 16px 16px;
492
+ max-height: 50vh;
493
+ }
494
+
495
+ .language-item {
496
+ font-family: PingFang SC;
497
+ font-weight: 400;
498
+ font-style: Regular;
499
+ font-size: 16px;
500
+ leading-trim: NONE;
501
+ line-height: 100%;
502
+ letter-spacing: 0px;
503
+ text-align: center;
504
+ text-transform: capitalize;
505
+ display: flex;
506
+ align-items: center;
507
+ justify-content: center;
508
+ padding: 16px 0;
509
+ border-bottom: 1px solid #f0f0f0;
510
+ cursor: pointer;
511
+ transition: background-color 0.2s;
512
+
513
+ &:active {
514
+ background-color: #f8f8f8;
515
+ }
516
+
517
+ &.selected {
518
+ color: #1677ff;
519
+ }
520
+
521
+ span {
522
+ font-size: 16px;
523
+ color: inherit;
524
+ }
525
+
526
+ &:last-child {
527
+ border-bottom: none;
528
+ }
529
+ }
530
+
531
+ .check-icon {
532
+ flex-shrink: 0;
533
+ }
534
+ </style>