@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.
- package/package.json +2 -2
- package/src/Components/TUICallKit.vue +1 -1
- package/src/Components/assets/aiAssistant/desktop/subtitleSettings.svg +4 -0
- package/src/Components/assets/aiAssistant/mobile/close-aiAssistant.svg +7 -0
- package/src/Components/assets/aiAssistant/mobile/open-aiAssistant.svg +7 -0
- package/src/Components/assets/aiAssistant/mobile/subtitleSettings.svg +3 -0
- package/src/Components/components/base/CustomSelect/CustomSelect.ts +45 -0
- package/src/Components/components/base/CustomSelect/CustomSelect.vue +199 -0
- package/src/Components/components/common/AIAssistant/AISubtitle.vue +155 -37
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.vue +35 -0
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.vue +89 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleContent.vue +234 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.vue +534 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.vue +294 -0
- package/src/Components/components/common/TopBar/TopBar.vue +41 -15
- package/src/Components/hooks/index.ts +1 -0
- package/src/Components/hooks/useAIAssistant.ts +142 -0
- package/src/Components/hooks/useGetVolumeMap.ts +2 -2
- package/src/TUICallService/CallService/AIAssistant.ts +285 -39
- package/src/TUICallService/CallService/UIKitModal.ts +14 -6
- package/src/TUICallService/CallService/bellContext.ts +25 -2
- package/src/TUICallService/CallService/engineEventHandler.ts +6 -1
- package/src/TUICallService/CallService/index.ts +72 -39
- package/src/TUICallService/CallService/miniProgram.ts +0 -12
- package/src/TUICallService/TUIStore/callStore.ts +6 -1
- package/src/TUICallService/UIKitModal/UIKitModal.ts +117 -0
- package/src/TUICallService/UIKitModal/index.ts +2 -0
- package/src/TUICallService/UIKitModal/type.ts +15 -0
- package/src/TUICallService/const/index.ts +4 -0
- package/src/TUICallService/interface/ICallStore.ts +5 -0
- package/src/TUICallService/locales/en.ts +15 -0
- package/src/TUICallService/locales/ja_JP.ts +15 -0
- package/src/TUICallService/locales/zh-cn.ts +15 -0
- package/src/TUICallService/utils/common-utils.ts +1 -1
- package/src/index.ts +1 -1
- package/stats.html +1 -1
- package/tuicall-uikit-vue.es.js +4199 -3474
- package/tuicall-uikit-vue.umd.js +2 -2
- package/types/Components/components/base/CustomSelect/CustomSelect.d.ts +41 -0
- package/types/Components/hooks/index.d.ts +1 -0
- package/types/Components/hooks/useAIAssistant.d.ts +19 -0
- package/types/TUICallService/CallService/AIAssistant.d.ts +72 -15
- package/types/TUICallService/CallService/bellContext.d.ts +3 -0
- package/types/TUICallService/CallService/index.d.ts +4 -3
- package/types/TUICallService/CallService/miniProgram.d.ts +0 -1
- package/types/TUICallService/UIKitModal/UIKitModal.d.ts +4 -0
- package/types/TUICallService/UIKitModal/index.d.ts +2 -0
- package/types/TUICallService/UIKitModal/type.d.ts +13 -0
- package/types/TUICallService/interface/ICallStore.d.ts +4 -0
- package/types/TUICallService/locales/en.d.ts +14 -0
- package/types/TUICallService/locales/ja_JP.d.ts +14 -0
- package/types/TUICallService/locales/zh-cn.d.ts +14 -0
- package/types/tsconfig.tsbuildinfo +1 -1
- package/src/Components/components/common/AIAssistant/AIAssistant.ts +0 -130
- package/src/Components/components/common/AIAssistant/index.ts +0 -11
- package/types/Components/components/common/AIAssistant/AIAssistant.d.ts +0 -40
- 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>
|