@chat21/chat21-web-widget 5.1.34-rc1 → 5.1.34
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/.github/workflows/docker-community-push-latest.yml +13 -23
- package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
- package/CHANGELOG.md +8 -129
- package/Dockerfile +5 -4
- package/angular.json +3 -21
- package/docs/changelog/this-branch.md +0 -36
- package/env.sample +2 -3
- package/nginx.conf +2 -22
- package/package.json +3 -10
- package/src/app/app.component.html +2 -2
- package/src/app/app.component.scss +14 -25
- package/src/app/app.component.spec.ts +6 -21
- package/src/app/app.component.ts +9 -10
- package/src/app/app.module.ts +0 -13
- package/src/app/component/conversation-detail/conversation/conversation.component.html +11 -25
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -40
- package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +75 -644
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +14 -100
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +13 -25
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +5 -123
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +0 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +10 -23
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -19
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +149 -242
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +5 -8
- package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +3 -53
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +96 -200
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +6 -211
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +78 -452
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +76 -291
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +53 -113
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +4 -12
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +29 -274
- package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +9 -23
- package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +8 -80
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +23 -29
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +16 -185
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +14 -34
- package/src/app/component/error-alert/error-alert.component.spec.ts +5 -65
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +7 -16
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +0 -21
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +7 -89
- package/src/app/component/form/form-builder/form-builder.component.html +1 -1
- package/src/app/component/form/form-builder/form-builder.component.spec.ts +21 -163
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +4 -8
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +5 -10
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +16 -90
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +0 -26
- package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +11 -45
- package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +6 -24
- package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +5 -14
- package/src/app/component/form/inputs/form-text/form-text.component.html +12 -14
- package/src/app/component/form/inputs/form-text/form-text.component.scss +1 -11
- package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +17 -113
- package/src/app/component/form/inputs/form-text/form-text.component.ts +3 -35
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +11 -13
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +5 -6
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +13 -149
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +0 -26
- package/src/app/component/form/prechat-form/prechat-form.component.html +11 -14
- package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +10 -102
- package/src/app/component/form/prechat-form/prechat-form.component.ts +1 -8
- package/src/app/component/home/home.component.html +31 -38
- package/src/app/component/home/home.component.scss +2 -4
- package/src/app/component/home/home.component.spec.ts +11 -226
- package/src/app/component/home-conversations/home-conversations.component.html +26 -30
- package/src/app/component/home-conversations/home-conversations.component.scss +0 -3
- package/src/app/component/home-conversations/home-conversations.component.spec.ts +36 -212
- package/src/app/component/last-message/last-message.component.html +9 -15
- package/src/app/component/last-message/last-message.component.scss +2 -16
- package/src/app/component/last-message/last-message.component.spec.ts +23 -204
- package/src/app/component/last-message/last-message.component.ts +1 -4
- package/src/app/component/launcher-button/launcher-button.component.html +13 -8
- package/src/app/component/launcher-button/launcher-button.component.spec.ts +8 -104
- package/src/app/component/list-all-conversations/list-all-conversations.component.html +17 -12
- package/src/app/component/list-all-conversations/list-all-conversations.component.scss +0 -2
- package/src/app/component/list-conversations/list-conversations.component.html +22 -22
- package/src/app/component/menu-options/menu-options.component.html +20 -30
- package/src/app/component/menu-options/menu-options.component.spec.ts +9 -125
- package/src/app/component/message/audio/audio.component.html +15 -13
- package/src/app/component/message/audio/audio.component.spec.ts +5 -140
- package/src/app/component/message/audio/audio.component.ts +5 -1
- package/src/app/component/message/avatar/avatar.component.html +2 -2
- package/src/app/component/message/avatar/avatar.component.spec.ts +7 -99
- package/src/app/component/message/bubble-message/bubble-message.component.html +51 -38
- package/src/app/component/message/bubble-message/bubble-message.component.scss +1 -54
- package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +57 -154
- package/src/app/component/message/bubble-message/bubble-message.component.ts +11 -89
- package/src/app/component/message/buttons/action-button/action-button.component.html +4 -3
- package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +5 -49
- package/src/app/component/message/buttons/link-button/link-button.component.scss +8 -5
- package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +5 -50
- package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +5 -44
- package/src/app/component/message/carousel/carousel.component.html +16 -29
- package/src/app/component/message/carousel/carousel.component.scss +8 -20
- package/src/app/component/message/carousel/carousel.component.spec.ts +3 -80
- package/src/app/component/message/carousel/carousel.component.ts +0 -16
- package/src/app/component/message/frame/frame.component.html +4 -9
- package/src/app/component/message/frame/frame.component.spec.ts +15 -34
- package/src/app/component/message/frame/frame.component.ts +2 -7
- package/src/app/component/message/html/html.component.html +1 -1
- package/src/app/component/message/html/html.component.scss +1 -1
- package/src/app/component/message/html/html.component.spec.ts +7 -24
- package/src/app/component/message/image/image.component.html +10 -12
- package/src/app/component/message/image/image.component.scss +0 -16
- package/src/app/component/message/image/image.component.spec.ts +15 -101
- package/src/app/component/message/image/image.component.ts +51 -90
- package/src/app/component/message/info-message/info-message.component.spec.ts +14 -26
- package/src/app/component/message/like-unlike/like-unlike.component.html +9 -7
- package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +3 -31
- package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +17 -38
- package/src/app/component/message/text/text.component.html +3 -3
- package/src/app/component/message/text/text.component.scss +86 -80
- package/src/app/component/message/text/text.component.spec.ts +13 -106
- package/src/app/component/message-attachment/message-attachment.component.spec.ts +13 -134
- package/src/app/component/selection-department/selection-department.component.html +23 -21
- package/src/app/component/selection-department/selection-department.component.spec.ts +14 -159
- package/src/app/component/selection-department/selection-department.component.ts +1 -8
- package/src/app/component/send-button/send-button.component.html +13 -5
- package/src/app/component/send-button/send-button.component.spec.ts +2 -2
- package/src/app/component/star-rating-widget/star-rating-widget.component.html +81 -51
- package/src/app/directives/tooltip.directive.spec.ts +4 -8
- package/src/app/modals/confirm-close/confirm-close.component.html +8 -20
- package/src/app/modals/confirm-close/confirm-close.component.scss +0 -3
- package/src/app/modals/confirm-close/confirm-close.component.spec.ts +4 -13
- package/src/app/modals/confirm-close/confirm-close.component.ts +1 -8
- package/src/app/pipe/html-entites-encode.pipe.spec.ts +2 -35
- package/src/app/pipe/marked.pipe.spec.ts +2 -38
- package/src/app/pipe/marked.pipe.ts +41 -51
- package/src/app/providers/app-config.service.ts +2 -4
- package/src/app/providers/brand.service.spec.ts +2 -23
- package/src/app/providers/brand.service.ts +1 -1
- package/src/app/providers/global-settings.service.spec.ts +14 -1009
- package/src/app/providers/global-settings.service.ts +2 -82
- package/src/app/providers/translator.service.ts +6 -26
- package/src/app/sass/_variables.scss +0 -3
- package/src/app/sass/animations.scss +1 -19
- package/src/app/utils/globals.ts +1 -21
- package/src/app/utils/utils-resources.ts +1 -1
- package/src/assets/i18n/en.json +99 -106
- package/src/assets/i18n/es.json +100 -107
- package/src/assets/i18n/fr.json +100 -107
- package/src/assets/i18n/it.json +98 -107
- package/src/assets/twp/index-dev.html +0 -18
- package/src/chat21-core/models/message.ts +1 -2
- package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +2 -3
- package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +0 -12
- package/src/chat21-core/providers/scripts/script.service.spec.ts +2 -12
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
- package/src/chat21-core/utils/utils-message.ts +0 -7
- package/src/chat21-core/utils/utils.ts +2 -5
- package/src/widget-config-template.json +1 -4
- package/src/widget-config.json +1 -4
- package/tsconfig.json +0 -5
- package/.angular-mcp-cache/package.json +0 -1
- package/.cursor/angular18-accessibility-auditor-skill.md +0 -442
- package/.cursor/mcp.json +0 -15
- package/.github/workflows/build.yml +0 -22
- package/.github/workflows/playwright.yml +0 -27
- package/mocks/voice-websocket-mock/server.cjs +0 -245
- package/playwright.config.ts +0 -41
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +0 -46
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +0 -83
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +0 -192
- package/src/app/component/form/prechat-form-test-mock.ts +0 -35
- package/src/app/component/message/audio-sync/audio-sync.component.html +0 -18
- package/src/app/component/message/audio-sync/audio-sync.component.scss +0 -65
- package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +0 -103
- package/src/app/component/message/audio-sync/audio-sync.component.ts +0 -643
- package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +0 -117
- package/src/app/providers/tts-audio-playback-coordinator.service.ts +0 -109
- package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +0 -12
- package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +0 -171
- package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +0 -39
- package/src/app/providers/voice/audio.types.ts +0 -40
- package/src/app/providers/voice/vad.service.spec.ts +0 -28
- package/src/app/providers/voice/vad.service.ts +0 -70
- package/src/app/providers/voice/voice-streaming.service.spec.ts +0 -23
- package/src/app/providers/voice/voice-streaming.service.ts +0 -702
- package/src/app/providers/voice/voice-streaming.types.ts +0 -112
- package/src/app/providers/voice/voice.service.spec.ts +0 -227
- package/src/app/providers/voice/voice.service.ts +0 -973
- package/src/app/shims/onnxruntime-web-wasm.ts +0 -4
- package/src/assets/onnx/ort-wasm-simd-threaded.mjs +0 -59
- package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
- package/src/assets/sounds/keyboard.mp3 +0 -0
- package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +0 -14
- package/src/assets/vad/silero_vad_legacy.onnx +0 -0
- package/src/assets/vad/vad.worklet.bundle.min.js +0 -1
- package/src/chat21-core/providers/chat-manager.spec.ts +0 -72
- package/tests/widget-form-rich.spec.ts +0 -67
- package/tests/widget-index-dev-settings.spec.ts +0 -52
- package/tests/widget-twp-iframe.spec.ts +0 -39
|
@@ -161,11 +161,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
161
161
|
membersConversation = ['SYSTEM'];
|
|
162
162
|
// ========== end:: typying =======
|
|
163
163
|
|
|
164
|
-
// ========== begin:: stream audio ======= //
|
|
165
|
-
public isStreamAudioActive = false;
|
|
166
|
-
public isStreamAudioConnecting = false;
|
|
167
|
-
// ========== end:: stream audio ======= //
|
|
168
|
-
|
|
169
164
|
@ViewChild(ConversationFooterComponent) conversationFooter: ConversationFooterComponent
|
|
170
165
|
@ViewChild(ConversationContentComponent) conversationContent: ConversationContentComponent
|
|
171
166
|
conversationHandlerService: ConversationHandlerService
|
|
@@ -251,22 +246,7 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
251
246
|
'CONTINUE',
|
|
252
247
|
'EMOJI_NOT_ELLOWED',
|
|
253
248
|
'ATTACHMENT',
|
|
254
|
-
'EMOJI'
|
|
255
|
-
'BUTTON_ATTACH_FILE',
|
|
256
|
-
'BUTTON_SEND_MESSAGE',
|
|
257
|
-
'BUTTON_RECORD_AUDIO',
|
|
258
|
-
'BUTTON_DELETE_AUDIO',
|
|
259
|
-
'BUTTON_SEND_AUDIO',
|
|
260
|
-
'BUTTON_PLAY_AUDIO',
|
|
261
|
-
'BUTTON_PAUSE_AUDIO',
|
|
262
|
-
'SKIP_TO_COMPOSER',
|
|
263
|
-
'CLOSE_CHAT',
|
|
264
|
-
'CLOSE',
|
|
265
|
-
'VOICE_CONNECTING',
|
|
266
|
-
'VOICE_LISTENING',
|
|
267
|
-
'VOICE_PROCESSING',
|
|
268
|
-
'STREAM_AUDIO',
|
|
269
|
-
'MAX_ATTACHMENT'
|
|
249
|
+
'EMOJI'
|
|
270
250
|
];
|
|
271
251
|
|
|
272
252
|
const keysContent = [
|
|
@@ -286,21 +266,13 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
286
266
|
'LABEL_THINKING',
|
|
287
267
|
'LABEL_TO',
|
|
288
268
|
'ARRAY_DAYS',
|
|
289
|
-
'CONVERSATION_LOG_LABEL',
|
|
290
|
-
'BUTTON_SCROLL_TO_BOTTOM',
|
|
291
|
-
'CAROUSEL_PREVIOUS',
|
|
292
|
-
'CAROUSEL_NEXT',
|
|
293
|
-
'CAROUSEL_LABEL',
|
|
294
|
-
'CAROUSEL_SLIDE_LABEL'
|
|
295
269
|
];
|
|
296
270
|
|
|
297
271
|
const keysPreview= [
|
|
298
272
|
'BACK',
|
|
299
273
|
'CLOSE',
|
|
300
274
|
'LABEL_PLACEHOLDER',
|
|
301
|
-
'LABEL_PREVIEW'
|
|
302
|
-
'BUTTON_CLOSE_PREVIEW',
|
|
303
|
-
'BUTTON_SEND_MESSAGE'
|
|
275
|
+
'LABEL_PREVIEW'
|
|
304
276
|
];
|
|
305
277
|
|
|
306
278
|
const keysCloseChatDialog= [
|
|
@@ -529,29 +501,25 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
529
501
|
return this.isConversationArchived;
|
|
530
502
|
}
|
|
531
503
|
|
|
532
|
-
//
|
|
533
|
-
|
|
534
|
-
try {
|
|
535
|
-
requests_list = await this.tiledeskRequestService.getMyRequests();
|
|
536
|
-
} catch (err) {
|
|
504
|
+
//FALLBACK TO TILEDESK
|
|
505
|
+
const requests_list = await this.tiledeskRequestService.getMyRequests().catch(err => {
|
|
537
506
|
this.logger.error('[CONV-COMP] getConversationDetail: error getting request from Tiledesk', err);
|
|
538
|
-
this.isConversationArchived
|
|
539
|
-
return
|
|
540
|
-
}
|
|
541
|
-
|
|
507
|
+
this.isConversationArchived=true
|
|
508
|
+
return { requests: [] }
|
|
509
|
+
});
|
|
542
510
|
if (requests_list && requests_list.requests.length > 0) {
|
|
543
511
|
this.logger.debug('[CONV-COMP] getConversationDetail: request exist on Tiledesk', requests_list);
|
|
544
|
-
|
|
545
|
-
if
|
|
546
|
-
this.isConversationArchived = false
|
|
547
|
-
return this.isConversationArchived
|
|
512
|
+
let conversation = requests_list.requests.find((request)=> request.request_id === this.conversationId)
|
|
513
|
+
if(conversation){
|
|
514
|
+
this.isConversationArchived = false
|
|
515
|
+
return this.isConversationArchived
|
|
548
516
|
}
|
|
549
517
|
this.logger.debug('[CONV-COMP] getConversationDetail: request NOT EXIST on Tiledesk', requests_list);
|
|
550
|
-
this.isConversationArchived = true
|
|
551
|
-
return this.isConversationArchived
|
|
518
|
+
this.isConversationArchived = true
|
|
519
|
+
return this.isConversationArchived
|
|
552
520
|
}
|
|
553
521
|
|
|
554
|
-
this.isConversationArchived =
|
|
522
|
+
this.isConversationArchived = true;
|
|
555
523
|
return null;
|
|
556
524
|
}
|
|
557
525
|
|
|
@@ -854,10 +822,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
854
822
|
this.showThinkingMessage = false;
|
|
855
823
|
}
|
|
856
824
|
|
|
857
|
-
if (this.isStreamAudioActive && msg.sender !== this.senderId) {
|
|
858
|
-
this.conversationFooter?.interruptStreamDueToPeerMessage();
|
|
859
|
-
}
|
|
860
|
-
|
|
861
825
|
that.newMessageAdded(msg);
|
|
862
826
|
// Update badge based on the latest message received from the server.
|
|
863
827
|
// We rely on `messages` being kept in-sync by the conversation handler.
|
|
@@ -913,20 +877,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
913
877
|
this.subscriptions.push(subscribe);
|
|
914
878
|
}
|
|
915
879
|
|
|
916
|
-
subscribtionKey = 'conversationsAdded';
|
|
917
|
-
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
918
|
-
if(!subscribtion){
|
|
919
|
-
|
|
920
|
-
subscribtion = this.chatManager.conversationsHandlerService.conversationChanged.pipe(takeUntil(this.unsubscribe$)).subscribe((conversation) => {
|
|
921
|
-
this.logger.debug('[CONV-COMP] ***** DATAIL conversationsChanged *****', conversation, this.conversationWith, this.isConversationArchived);
|
|
922
|
-
if(conversation && conversation.recipient === this.conversationId){
|
|
923
|
-
this.isConversationArchived = false
|
|
924
|
-
}
|
|
925
|
-
});
|
|
926
|
-
const subscribe = {key: subscribtionKey, value: subscribtion };
|
|
927
|
-
this.subscriptions.push(subscribe);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
880
|
subscribtionKey = 'messageWait';
|
|
931
881
|
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
932
882
|
if (!subscribtion) {
|
|
@@ -1082,21 +1032,6 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1082
1032
|
|
|
1083
1033
|
|
|
1084
1034
|
|
|
1085
|
-
/**
|
|
1086
|
-
* Programmatically moves keyboard focus to the message composer textarea.
|
|
1087
|
-
* Wired to the visible-on-focus skip link in conversation.component.html (WCAG 2.4.1 Bypass Blocks).
|
|
1088
|
-
*/
|
|
1089
|
-
skipToCompose() {
|
|
1090
|
-
try {
|
|
1091
|
-
const textarea = document.getElementById('chat21-main-message-context') as HTMLTextAreaElement | null;
|
|
1092
|
-
if (textarea) {
|
|
1093
|
-
textarea.focus();
|
|
1094
|
-
}
|
|
1095
|
-
} catch(e) {
|
|
1096
|
-
this.logger.warn('[CONV-COMP] skipToCompose error', e);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
1035
|
scrollToBottom() {
|
|
1101
1036
|
this.conversationContent.scrollToBottom();
|
|
1102
1037
|
// const that = this;
|
|
@@ -1448,29 +1383,8 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1448
1383
|
this.logger.debug('[CONV-COMP] floating onNewConversationButtonClicked')
|
|
1449
1384
|
this.onNewConversationButtonClicked.emit()
|
|
1450
1385
|
}
|
|
1451
|
-
|
|
1452
|
-
/** CALLED BY: conv-footer streaming audio button */
|
|
1453
|
-
onStreamAudioActiveChange(event: boolean){
|
|
1454
|
-
this.isStreamAudioActive = event
|
|
1455
|
-
}
|
|
1456
|
-
/** CALLED BY: conv-footer when connecting state changes */
|
|
1457
|
-
onStreamAudioConnectingChange(event: boolean){
|
|
1458
|
-
this.isStreamAudioConnecting = event
|
|
1459
|
-
}
|
|
1460
|
-
/** CALLED BY: conv-footer component */
|
|
1461
|
-
onCloseChatButtonClickedFN(event){
|
|
1462
|
-
this.logger.debug('[CONV-COMP] onCloseChatButtonClicked::::', event)
|
|
1463
|
-
this.onCloseChat()
|
|
1464
|
-
}
|
|
1465
1386
|
// =========== END: event emitter function ====== //
|
|
1466
1387
|
|
|
1467
|
-
/**
|
|
1468
|
-
* True quando è visibile il pulsante chiudi stream (`.close-stream-button`, `isStreamAudioActive`).
|
|
1469
|
-
* Solo in quel caso il bottom del foglio include `--chat-footer-stream-button-height`.
|
|
1470
|
-
*/
|
|
1471
|
-
closeStreamButtonActiveForSheetBottom(): boolean {
|
|
1472
|
-
return !!(this.g?.showAudioStreamFooterButton && (this.isStreamAudioActive || this.isStreamAudioConnecting));
|
|
1473
|
-
}
|
|
1474
1388
|
|
|
1475
1389
|
openInputFiles() {
|
|
1476
1390
|
alert('ok');
|
|
@@ -1,44 +1,32 @@
|
|
|
1
1
|
<div class="audio-recorder">
|
|
2
|
-
<button *ngIf="audioUrl"
|
|
2
|
+
<button *ngIf="audioUrl" (click)="deleteRecording()">
|
|
3
3
|
<span class="v-align-center">
|
|
4
|
-
<svg
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
|
|
5
5
|
<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm80-160h80v-360h-80v360Zm160 0h80v-360h-80v360Z"/>
|
|
6
6
|
</svg>
|
|
7
|
+
<!-- <i class="material-icons">delete_outline</i> -->
|
|
7
8
|
</span>
|
|
8
9
|
</button>
|
|
9
10
|
|
|
10
|
-
<chat-audio
|
|
11
|
-
[audioBlob]="audioBlob"
|
|
11
|
+
<chat-audio class="test" *ngIf="audioBlob && audioUrl"
|
|
12
|
+
[audioBlob] = "audioBlob"
|
|
12
13
|
[color]="'var(--chat-footer-color)'"
|
|
13
|
-
[translationMap]="translationMap"
|
|
14
14
|
[stylesMap]="stylesMap">
|
|
15
15
|
</chat-audio>
|
|
16
|
-
|
|
17
|
-
<button *ngIf="!audioUrl"
|
|
18
|
-
|
|
19
|
-
class="mic-button"
|
|
20
|
-
[attr.aria-label]="translationMap?.get('BUTTON_RECORD_AUDIO') || 'Hold to record an audio message'"
|
|
21
|
-
[attr.aria-pressed]="isRecording ? 'true' : 'false'"
|
|
22
|
-
(mousedown)="startRecording($event)"
|
|
23
|
-
(mouseup)="stopRecording($event)"
|
|
24
|
-
(touchstart)="startRecording($event)"
|
|
25
|
-
(touchend)="stopRecording($event)"
|
|
26
|
-
(keydown.space)="$event.preventDefault(); !isRecording && startRecording($event)"
|
|
27
|
-
(keyup.space)="$event.preventDefault(); isRecording && stopRecording($event)">
|
|
28
|
-
<svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
|
|
16
|
+
|
|
17
|
+
<button *ngIf="!audioUrl" class="mic-button" (mousedown)="startRecording($event)" (mouseup)="stopRecording($event)" (touchstart)="startRecording($event)" (touchend)="stopRecording($event)">
|
|
18
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
|
|
29
19
|
<path d="M480-400q-50 0-85-35t-35-85v-240q0-50 35-85t85-35q50 0 85 35t35 85v240q0 50-35 85t-85 35Zm0-240Zm-40 520v-123q-104-14-172-93t-68-184h80q0 83 58.5 141.5T480-320q83 0 141.5-58.5T680-520h80q0 105-68 184t-172 93v123h-80Zm40-360q17 0 28.5-11.5T520-520v-240q0-17-11.5-28.5T480-800q-17 0-28.5 11.5T440-760v240q0 17 11.5 28.5T480-480Z"/>
|
|
30
20
|
</svg>
|
|
31
21
|
</button>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
[attr.aria-label]="translationMap?.get('BUTTON_SEND_AUDIO') || 'Send audio message'"
|
|
36
|
-
(click)="sendMessage()">
|
|
22
|
+
<!-- <button *ngIf="isRecording" (click)="stopRecording()"><i class="material-icons">pause_circle_outline</i></button> -->
|
|
23
|
+
|
|
24
|
+
<button *ngIf="audioUrl" (click)="sendMessage()">
|
|
37
25
|
<span class="v-align-center">
|
|
38
|
-
<svg
|
|
26
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="20" width="24" viewBox="0 0 24 20" xml:space="preserve">
|
|
39
27
|
<path d="M1.8,18.9V1.7L22,10.3L1.8,18.9z M3.9,15.6l12.6-5.4L3.9,4.9v3.7l6.4,1.6l-6.4,1.6V15.6z M3.9,15.6V4.9v7V15.6z"/>
|
|
40
28
|
</svg>
|
|
41
29
|
</span>
|
|
42
30
|
</button>
|
|
43
31
|
|
|
44
|
-
</div>
|
|
32
|
+
</div>
|
|
@@ -1,141 +1,23 @@
|
|
|
1
|
-
import { ComponentFixture,
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
2
|
|
|
3
3
|
import { ConversationAudioRecorderComponent } from './conversation-audio-recorder.component';
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('AudioRecorderComponent', () => {
|
|
6
6
|
let component: ConversationAudioRecorderComponent;
|
|
7
7
|
let fixture: ComponentFixture<ConversationAudioRecorderComponent>;
|
|
8
|
-
let stopListeners: { stop?: () => void; data?: (e: { data: Blob }) => void };
|
|
9
|
-
let mediaRecorderInstance: {
|
|
10
|
-
start: jasmine.Spy;
|
|
11
|
-
stop: jasmine.Spy;
|
|
12
|
-
mimeType: string;
|
|
13
|
-
addEventListener: jasmine.Spy;
|
|
14
|
-
};
|
|
15
8
|
|
|
16
9
|
beforeEach(async () => {
|
|
17
|
-
stopListeners = {};
|
|
18
|
-
mediaRecorderInstance = {
|
|
19
|
-
start: jasmine.createSpy('start'),
|
|
20
|
-
stop: jasmine.createSpy('stop').and.callFake(() => {
|
|
21
|
-
const fn = stopListeners.stop;
|
|
22
|
-
if (fn) {
|
|
23
|
-
fn();
|
|
24
|
-
}
|
|
25
|
-
}),
|
|
26
|
-
mimeType: 'audio/webm',
|
|
27
|
-
addEventListener: jasmine.createSpy('addEventListener').and.callFake((ev: string, fn: any) => {
|
|
28
|
-
if (ev === 'stop') {
|
|
29
|
-
stopListeners.stop = fn;
|
|
30
|
-
}
|
|
31
|
-
if (ev === 'dataavailable') {
|
|
32
|
-
stopListeners.data = fn;
|
|
33
|
-
}
|
|
34
|
-
}),
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const stream = {
|
|
38
|
-
getTracks: () => [{ stop: jasmine.createSpy('trackStop') }],
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
spyOn(window.navigator.mediaDevices, 'getUserMedia').and.returnValue(Promise.resolve(stream as any));
|
|
42
|
-
(window as any).MediaRecorder = jasmine.createSpy('MediaRecorder').and.returnValue(mediaRecorderInstance);
|
|
43
|
-
|
|
44
10
|
await TestBed.configureTestingModule({
|
|
45
|
-
declarations: [ConversationAudioRecorderComponent]
|
|
46
|
-
})
|
|
11
|
+
declarations: [ ConversationAudioRecorderComponent ]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
47
14
|
|
|
48
15
|
fixture = TestBed.createComponent(ConversationAudioRecorderComponent);
|
|
49
16
|
component = fixture.componentInstance;
|
|
50
|
-
component.translationMap = new Map();
|
|
51
|
-
component.stylesMap = new Map();
|
|
52
|
-
spyOn(component.startRecordingEvent, 'emit');
|
|
53
|
-
spyOn(component.endRecordingEvent, 'emit');
|
|
54
17
|
fixture.detectChanges();
|
|
55
18
|
});
|
|
56
19
|
|
|
57
20
|
it('should create', () => {
|
|
58
21
|
expect(component).toBeTruthy();
|
|
59
22
|
});
|
|
60
|
-
|
|
61
|
-
describe('startRecording', () => {
|
|
62
|
-
it('should preventDefault on touchstart', fakeAsync(() => {
|
|
63
|
-
const ev = { type: 'touchstart', preventDefault: jasmine.createSpy('pd') } as any;
|
|
64
|
-
component.startRecording(ev);
|
|
65
|
-
tick();
|
|
66
|
-
expect(ev.preventDefault).toHaveBeenCalled();
|
|
67
|
-
expect(component.startRecordingEvent.emit).toHaveBeenCalled();
|
|
68
|
-
}));
|
|
69
|
-
|
|
70
|
-
it('should request microphone and start MediaRecorder on mousedown', fakeAsync(() => {
|
|
71
|
-
const ev = new MouseEvent('mousedown');
|
|
72
|
-
component.startRecording(ev);
|
|
73
|
-
tick();
|
|
74
|
-
expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith({ audio: true });
|
|
75
|
-
expect(mediaRecorderInstance.start).toHaveBeenCalled();
|
|
76
|
-
expect(component.isRecording).toBe(true);
|
|
77
|
-
}));
|
|
78
|
-
|
|
79
|
-
it('should log when getUserMedia fails', fakeAsync(() => {
|
|
80
|
-
(navigator.mediaDevices.getUserMedia as jasmine.Spy).and.returnValue(Promise.reject(new Error('denied')));
|
|
81
|
-
spyOn(console, 'error');
|
|
82
|
-
component.startRecording(new MouseEvent('mousedown'));
|
|
83
|
-
tick();
|
|
84
|
-
expect(console.error).toHaveBeenCalled();
|
|
85
|
-
}));
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('stopRecording', () => {
|
|
89
|
-
it('should discard very short press without stopping recorder', fakeAsync(() => {
|
|
90
|
-
component.startTime = Date.now();
|
|
91
|
-
component.stopRecording(new MouseEvent('mouseup'));
|
|
92
|
-
tick(400);
|
|
93
|
-
expect(mediaRecorderInstance.stop).not.toHaveBeenCalled();
|
|
94
|
-
}));
|
|
95
|
-
|
|
96
|
-
it('should stop recorder after long press', fakeAsync(() => {
|
|
97
|
-
component.mediaRecorder = mediaRecorderInstance as any;
|
|
98
|
-
component.startTime = Date.now() - 600;
|
|
99
|
-
component.stopRecording(new MouseEvent('mouseup'));
|
|
100
|
-
tick(400);
|
|
101
|
-
expect(mediaRecorderInstance.stop).toHaveBeenCalled();
|
|
102
|
-
}));
|
|
103
|
-
|
|
104
|
-
it('should preventDefault on touchend', () => {
|
|
105
|
-
const ev = { type: 'touchend', preventDefault: jasmine.createSpy('pd') } as any;
|
|
106
|
-
component.stopRecording(ev);
|
|
107
|
-
expect(ev.preventDefault).toHaveBeenCalled();
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe('deleteRecording', () => {
|
|
112
|
-
it('should reset state and emit', () => {
|
|
113
|
-
spyOn(component.deleteRecordingEvent, 'emit');
|
|
114
|
-
component.audioUrl = {} as any;
|
|
115
|
-
component.audioBlob = new Blob();
|
|
116
|
-
component.deleteRecording();
|
|
117
|
-
expect(component.audioUrl).toBeNull();
|
|
118
|
-
expect(component.audioBlob).toBeNull();
|
|
119
|
-
expect(component.deleteRecordingEvent.emit).toHaveBeenCalledWith(null);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('sendMessage', () => {
|
|
124
|
-
it('should emit blob and clear url when recording exists', () => {
|
|
125
|
-
spyOn(component.sendRecordingEvent, 'emit');
|
|
126
|
-
const b = new Blob(['a'], { type: 'audio/webm' });
|
|
127
|
-
component.audioBlob = b;
|
|
128
|
-
component.audioUrl = {} as any;
|
|
129
|
-
component.sendMessage();
|
|
130
|
-
expect(component.sendRecordingEvent.emit).toHaveBeenCalledWith(b);
|
|
131
|
-
expect(component.audioUrl).toBeNull();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should no-op when there is no audioUrl', () => {
|
|
135
|
-
spyOn(component.sendRecordingEvent, 'emit');
|
|
136
|
-
component.audioUrl = null;
|
|
137
|
-
component.sendMessage();
|
|
138
|
-
expect(component.sendRecordingEvent.emit).not.toHaveBeenCalled();
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
23
|
});
|
|
@@ -9,7 +9,6 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
|
|
9
9
|
export class ConversationAudioRecorderComponent {
|
|
10
10
|
|
|
11
11
|
@Input() stylesMap: Map<string, string>;
|
|
12
|
-
@Input() translationMap: Map<string, string>;
|
|
13
12
|
@Output() startRecordingEvent = new EventEmitter<void>();
|
|
14
13
|
@Output() deleteRecordingEvent = new EventEmitter<void>();
|
|
15
14
|
@Output() endRecordingEvent = new EventEmitter<Blob | null>();
|
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<div class="c21-body-container">
|
|
4
4
|
|
|
5
|
-
<div class="c21-body-content"
|
|
6
|
-
role="log"
|
|
7
|
-
aria-live="polite"
|
|
8
|
-
aria-relevant="additions text"
|
|
9
|
-
aria-atomic="false"
|
|
10
|
-
[attr.aria-label]="translationMap?.get('CONVERSATION_LOG_LABEL') || 'Conversation messages'">
|
|
5
|
+
<div class="c21-body-content" tabindex="1520" aria-label=" messaggi della conversazione: ">
|
|
11
6
|
|
|
12
7
|
<!-- USER TYPING (WAIT MESSAGE) -->
|
|
13
8
|
<span *ngIf="messages && this.messages.length === 0 && !isTypings">
|
|
@@ -24,22 +19,21 @@
|
|
|
24
19
|
<div #scrollMe id="scroll-me" (scroll)="onScroll($event)">
|
|
25
20
|
|
|
26
21
|
<div id="{{idDivScroll}}" class="c21-contentScroll" > <!-- (resized)="onResized($event)" -->
|
|
27
|
-
<div *ngFor="let message of messages; let first = first; let last = last; let i = index" class="rowMsg">
|
|
28
|
-
|
|
22
|
+
<div *ngFor="let message of messages; let first = first; let last = last; let i = index" tabindex="1521" class="rowMsg">
|
|
23
|
+
|
|
29
24
|
<!-- message SENDER:: -->
|
|
30
|
-
|
|
25
|
+
<div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_MINE, message)" class="msg_container base_sent">
|
|
31
26
|
|
|
32
27
|
<!--backgroundColor non viene ancora usato -->
|
|
33
28
|
<!-- class="messages msg_sent slide-in-right" -->
|
|
34
29
|
<chat-bubble-message class="messages msg_sent"
|
|
35
|
-
[class.no-background]="(isImage(message) || isFrame(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
30
|
+
[class.no-background]="(isImage(message) || isFrame(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
36
31
|
[class.emoticon]="isEmojii(message?.text)"
|
|
37
32
|
[ngStyle]="{'background': stylesMap.get('bubbleSentBackground'), 'color': stylesMap.get('bubbleSentTextColor')}"
|
|
38
33
|
[ngClass]="{'button-in-msg' : message?.metadata && message?.metadata?.button}"
|
|
39
34
|
[message]="message"
|
|
40
35
|
[fontColor]="stylesMap.get('bubbleSentTextColor')"
|
|
41
36
|
[stylesMap]="stylesMap"
|
|
42
|
-
[translationMap]="translationMap"
|
|
43
37
|
(onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
|
|
44
38
|
(onAfterMessageRender)="onAfterMessageRenderFN($event)"
|
|
45
39
|
(onElementRendered)="onElementRenderedFN($event)">
|
|
@@ -53,9 +47,9 @@
|
|
|
53
47
|
</div>
|
|
54
48
|
|
|
55
49
|
<!-- message RECIPIENT:: -->
|
|
56
|
-
<div role="
|
|
50
|
+
<div role="messaggio" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message)" class="msg_container base_receive">
|
|
57
51
|
|
|
58
|
-
<chat-avatar-image *ngIf="!isSameSender(message?.sender, i)
|
|
52
|
+
<chat-avatar-image *ngIf="!isSameSender(message?.sender, i)"
|
|
59
53
|
[ngClass]="{'slide-in-left': false}"
|
|
60
54
|
[senderID]="message?.sender"
|
|
61
55
|
[senderFullname]="message?.sender_fullname"
|
|
@@ -66,17 +60,14 @@
|
|
|
66
60
|
<!-- [ngClass]="{'slide-in-left': !isFirstMessage(message?.sender, i)}" -->
|
|
67
61
|
<chat-bubble-message class="messages msg_receive"
|
|
68
62
|
[ngClass]="{'slide-in-left': false}"
|
|
69
|
-
[class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
63
|
+
[class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
70
64
|
[class.emoticon]="isEmojii(message?.text)"
|
|
71
|
-
[
|
|
72
|
-
[style.margin-left]="isSameSender(message?.sender, i) ? 'calc(var(--avatar-width) + 10px)' : null"
|
|
65
|
+
[style.margin-left]="isSameSender(message?.sender, i)? 'calc(var(--avatar-width) + 10px)': null"
|
|
73
66
|
[ngStyle]="{'background': stylesMap.get('bubbleReceivedBackground'), 'color': stylesMap.get('bubbleReceivedTextColor'), 'width':isFrame(message) ?'100%' : null}"
|
|
74
67
|
[isSameSender]="isSameSender(message?.sender, i)"
|
|
75
68
|
[message]="message"
|
|
76
69
|
[fontColor]="stylesMap.get('bubbleReceivedTextColor')"
|
|
77
70
|
[stylesMap]="stylesMap"
|
|
78
|
-
[translationMap]="translationMap"
|
|
79
|
-
[streamOnArrival]="false"
|
|
80
71
|
(onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
|
|
81
72
|
(onAfterMessageRender)="onAfterMessageRenderFN($event)"
|
|
82
73
|
(onElementRendered)="onElementRenderedFN($event)">
|
|
@@ -119,7 +110,6 @@
|
|
|
119
110
|
[isConversationArchived]="isConversationArchived"
|
|
120
111
|
[isLastMessage] = "isLastMessage(message?.uid)"
|
|
121
112
|
[stylesMap]="stylesMap"
|
|
122
|
-
[translationMap]="translationMap"
|
|
123
113
|
(onElementRendered)="onElementRenderedFN($event)"
|
|
124
114
|
(onAttachmentButtonClicked)="onAttachmentButtonClickedFN($event)">
|
|
125
115
|
</chat-carousel>
|
|
@@ -144,10 +134,9 @@
|
|
|
144
134
|
[senderFullname]="nameUserTypingNow"
|
|
145
135
|
[baseLocation]="baseLocation">
|
|
146
136
|
</chat-avatar-image>
|
|
147
|
-
|
|
148
137
|
<user-typing
|
|
149
|
-
[color]="stylesMap?.get('iconColor')"
|
|
150
138
|
[ngClass]="{'userTypingNowExist': !idUserTypingNow}"
|
|
139
|
+
[color]="stylesMap?.get('iconColor')"
|
|
151
140
|
[translationMap]="translationMap"
|
|
152
141
|
[idUserTypingNow]="idUserTypingNow"
|
|
153
142
|
[nameUserTypingNow]="nameUserTypingNow">
|
|
@@ -156,9 +145,7 @@
|
|
|
156
145
|
|
|
157
146
|
<div *ngIf="showThinkingMessage && lastServerSenderKind === 'bot'" class="msg_container base_receive thinking_receive">
|
|
158
147
|
<user-typing class="loading thinking-dots"
|
|
159
|
-
[class.fullSize]="isStreamAudioActive"
|
|
160
148
|
[color]="stylesMap?.get('iconColor')"
|
|
161
|
-
[class.fullSize]="isStreamAudioActive"
|
|
162
149
|
[translationMap]="translationMap"
|
|
163
150
|
[idUserTypingNow]="idUserTypingNow"
|
|
164
151
|
[nameUserTypingNow]="nameUserTypingNow">
|
|
@@ -27,10 +27,6 @@
|
|
|
27
27
|
margin: 25px 50px
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
:host .loading.fullSize ::ng-deep > div.spinner{
|
|
31
|
-
margin: 15px 0px !important;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
30
|
// ============= CSS c21-body ================= //
|
|
35
31
|
.c21-body {
|
|
36
32
|
// -webkit-box-shadow: inset 0 10px 10px -10px rgba(0,0,0,0.4);
|
|
@@ -48,7 +44,7 @@
|
|
|
48
44
|
top: 0;
|
|
49
45
|
right: 0;
|
|
50
46
|
left: 0;
|
|
51
|
-
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height)
|
|
47
|
+
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
|
|
52
48
|
overflow: hidden;
|
|
53
49
|
.time{
|
|
54
50
|
margin-bottom: 20px;
|
|
@@ -246,11 +242,6 @@
|
|
|
246
242
|
height: fit-content;
|
|
247
243
|
width: auto;
|
|
248
244
|
|
|
249
|
-
&.fullSizeMessage {
|
|
250
|
-
max-width: 100%;
|
|
251
|
-
margin: auto 0 auto 0 !important;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
245
|
}
|
|
255
246
|
.msg_receive.json-resources{
|
|
256
247
|
min-height: unset;
|
|
@@ -292,15 +283,6 @@
|
|
|
292
283
|
}// end c21-body-container
|
|
293
284
|
}// end c21-body
|
|
294
285
|
|
|
295
|
-
/* Solo con pulsante chiudi stream (stream in ascolto): altezza extra come #streamAudioAlert */
|
|
296
|
-
:host-context(#chat21-conversation-component.chat21-conversation--close-stream-active) .c21-body .c21-body-container .c21-body-content .chat21-sheet-content {
|
|
297
|
-
bottom: calc(
|
|
298
|
-
var(--chat-footer-logo-height) +
|
|
299
|
-
var(--chat-footer-height) +
|
|
300
|
-
var(--chat-footer-stream-button-height)
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
286
|
@keyframes thinking-dot {
|
|
305
287
|
0%, 80%, 100% {
|
|
306
288
|
opacity: 0.2;
|