@chat21/chat21-web-widget 5.1.33 → 5.1.34-rc1
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/.angular-mcp-cache/package.json +1 -0
- package/.cursor/angular18-accessibility-auditor-skill.md +442 -0
- package/.cursor/mcp.json +15 -0
- package/.github/workflows/docker-community-push-latest.yml +23 -13
- package/.github/workflows/docker-image-tag-community-tag-push.yml +22 -12
- package/.github/workflows/playwright.yml +27 -0
- package/CHANGELOG.md +130 -6
- package/Dockerfile +4 -5
- package/angular.json +24 -4
- package/docs/changelog/this-branch.md +36 -0
- package/env.sample +3 -2
- package/mocks/voice-websocket-mock/server.cjs +245 -0
- package/nginx.conf +22 -2
- package/package.json +10 -3
- package/playwright.config.ts +41 -0
- package/src/app/app.component.html +2 -2
- package/src/app/app.component.scss +25 -14
- package/src/app/app.component.spec.ts +21 -6
- package/src/app/app.component.ts +10 -9
- package/src/app/app.module.ts +15 -0
- package/src/app/component/conversation-detail/conversation/conversation.component.html +25 -11
- package/src/app/component/conversation-detail/conversation/conversation.component.scss +40 -2
- package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +644 -75
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +100 -14
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +25 -13
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +123 -5
- package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +1 -0
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +23 -10
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +33 -2
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +242 -149
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +8 -6
- package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +53 -3
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +200 -96
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +211 -6
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +452 -78
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +291 -76
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +113 -53
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +12 -4
- package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +274 -29
- package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +23 -9
- package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +80 -8
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +29 -23
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +185 -16
- package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +34 -14
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +46 -0
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +83 -0
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +192 -0
- package/src/app/component/error-alert/error-alert.component.spec.ts +65 -5
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +16 -7
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +21 -0
- package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +89 -7
- 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 +163 -21
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +8 -4
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +10 -5
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +90 -16
- package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +26 -0
- package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +45 -11
- package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +24 -6
- package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +14 -5
- package/src/app/component/form/inputs/form-text/form-text.component.html +14 -12
- package/src/app/component/form/inputs/form-text/form-text.component.scss +11 -1
- package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +113 -17
- package/src/app/component/form/inputs/form-text/form-text.component.ts +35 -3
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +13 -11
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +6 -5
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +149 -13
- package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +26 -0
- package/src/app/component/form/prechat-form/prechat-form.component.html +14 -11
- package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +102 -10
- package/src/app/component/form/prechat-form/prechat-form.component.ts +8 -1
- package/src/app/component/form/prechat-form-test-mock.ts +35 -0
- package/src/app/component/home/home.component.html +38 -31
- package/src/app/component/home/home.component.scss +4 -2
- package/src/app/component/home/home.component.spec.ts +226 -11
- package/src/app/component/home-conversations/home-conversations.component.html +30 -26
- package/src/app/component/home-conversations/home-conversations.component.scss +3 -0
- package/src/app/component/home-conversations/home-conversations.component.spec.ts +212 -36
- package/src/app/component/last-message/last-message.component.html +15 -9
- package/src/app/component/last-message/last-message.component.scss +16 -2
- package/src/app/component/last-message/last-message.component.spec.ts +204 -23
- package/src/app/component/last-message/last-message.component.ts +4 -1
- package/src/app/component/launcher-button/launcher-button.component.html +8 -13
- package/src/app/component/launcher-button/launcher-button.component.spec.ts +104 -8
- package/src/app/component/list-all-conversations/list-all-conversations.component.html +12 -17
- package/src/app/component/list-all-conversations/list-all-conversations.component.scss +2 -0
- package/src/app/component/list-conversations/list-conversations.component.html +22 -22
- package/src/app/component/menu-options/menu-options.component.html +30 -20
- package/src/app/component/menu-options/menu-options.component.spec.ts +125 -9
- package/src/app/component/message/audio/audio.component.html +13 -15
- package/src/app/component/message/audio/audio.component.spec.ts +140 -5
- package/src/app/component/message/audio/audio.component.ts +1 -5
- package/src/app/component/message/audio-sync/audio-sync.component.html +18 -0
- package/src/app/component/message/audio-sync/audio-sync.component.scss +65 -0
- package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +103 -0
- package/src/app/component/message/audio-sync/audio-sync.component.ts +643 -0
- package/src/app/component/message/avatar/avatar.component.html +2 -2
- package/src/app/component/message/avatar/avatar.component.spec.ts +99 -7
- package/src/app/component/message/bubble-message/bubble-message.component.html +43 -51
- package/src/app/component/message/bubble-message/bubble-message.component.scss +59 -1
- package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +154 -57
- package/src/app/component/message/bubble-message/bubble-message.component.ts +152 -109
- package/src/app/component/message/buttons/action-button/action-button.component.html +3 -4
- package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +49 -5
- package/src/app/component/message/buttons/link-button/link-button.component.scss +5 -8
- package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +50 -5
- package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +44 -5
- package/src/app/component/message/carousel/carousel.component.html +29 -16
- package/src/app/component/message/carousel/carousel.component.scss +20 -8
- package/src/app/component/message/carousel/carousel.component.spec.ts +80 -3
- package/src/app/component/message/carousel/carousel.component.ts +16 -0
- package/src/app/component/message/frame/frame.component.html +9 -4
- package/src/app/component/message/frame/frame.component.spec.ts +34 -15
- package/src/app/component/message/frame/frame.component.ts +7 -2
- 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 +24 -7
- package/src/app/component/message/image/image.component.html +12 -10
- package/src/app/component/message/image/image.component.scss +16 -0
- package/src/app/component/message/image/image.component.spec.ts +101 -15
- package/src/app/component/message/image/image.component.ts +90 -51
- package/src/app/component/message/info-message/info-message.component.spec.ts +26 -14
- package/src/app/component/message/json-sources/json-sources.component.html +38 -0
- package/src/app/component/message/json-sources/json-sources.component.scss +201 -0
- package/src/app/component/message/json-sources/json-sources.component.ts +89 -0
- package/src/app/component/message/like-unlike/like-unlike.component.html +7 -9
- package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +31 -3
- package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +38 -17
- package/src/app/component/message/text/text.component.html +3 -3
- package/src/app/component/message/text/text.component.scss +80 -86
- package/src/app/component/message/text/text.component.spec.ts +106 -13
- package/src/app/component/message-attachment/message-attachment.component.spec.ts +134 -13
- package/src/app/component/selection-department/selection-department.component.html +21 -23
- package/src/app/component/selection-department/selection-department.component.spec.ts +159 -14
- package/src/app/component/selection-department/selection-department.component.ts +8 -1
- package/src/app/component/send-button/send-button.component.html +5 -13
- 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 +51 -81
- package/src/app/directives/tooltip.directive.spec.ts +8 -4
- package/src/app/modals/confirm-close/confirm-close.component.html +20 -8
- package/src/app/modals/confirm-close/confirm-close.component.scss +3 -0
- package/src/app/modals/confirm-close/confirm-close.component.spec.ts +13 -4
- package/src/app/modals/confirm-close/confirm-close.component.ts +8 -1
- package/src/app/pipe/html-entites-encode.pipe.spec.ts +35 -2
- package/src/app/pipe/marked.pipe.spec.ts +38 -2
- package/src/app/pipe/marked.pipe.ts +51 -41
- package/src/app/providers/app-config.service.ts +4 -2
- package/src/app/providers/brand.service.spec.ts +23 -2
- package/src/app/providers/brand.service.ts +1 -1
- package/src/app/providers/global-settings.service.spec.ts +1009 -14
- package/src/app/providers/global-settings.service.ts +82 -2
- package/src/app/providers/json-sources-parser.service.ts +175 -0
- package/src/app/providers/translator.service.ts +26 -6
- package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +117 -0
- package/src/app/providers/tts-audio-playback-coordinator.service.ts +109 -0
- package/src/app/providers/url-preview.service.ts +82 -0
- package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +12 -0
- package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +171 -0
- package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +39 -0
- package/src/app/providers/voice/audio.types.ts +40 -0
- package/src/app/providers/voice/vad.service.spec.ts +28 -0
- package/src/app/providers/voice/vad.service.ts +70 -0
- package/src/app/providers/voice/voice-streaming.service.spec.ts +23 -0
- package/src/app/providers/voice/voice-streaming.service.ts +702 -0
- package/src/app/providers/voice/voice-streaming.types.ts +112 -0
- package/src/app/providers/voice/voice.service.spec.ts +227 -0
- package/src/app/providers/voice/voice.service.ts +973 -0
- package/src/app/sass/_variables.scss +3 -0
- package/src/app/sass/animations.scss +19 -1
- package/src/app/shims/onnxruntime-web-wasm.ts +4 -0
- package/src/app/utils/globals.ts +21 -1
- package/src/app/utils/json-sources-utils.ts +27 -0
- package/src/app/utils/url-utils.ts +98 -0
- package/src/app/utils/utils-resources.ts +1 -1
- package/src/assets/i18n/en.json +106 -99
- package/src/assets/i18n/es.json +107 -100
- package/src/assets/i18n/fr.json +107 -100
- package/src/assets/i18n/it.json +107 -98
- package/src/assets/onnx/ort-wasm-simd-threaded.mjs +59 -0
- package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
- package/src/assets/sounds/keyboard.mp3 +0 -0
- package/src/assets/twp/chatbot-panel.html +3 -1
- package/src/assets/twp/index-dev.html +18 -0
- package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +14 -0
- package/src/assets/vad/silero_vad_legacy.onnx +0 -0
- package/src/assets/vad/vad.worklet.bundle.min.js +1 -0
- package/src/chat21-core/models/message.ts +2 -1
- package/src/chat21-core/providers/chat-manager.spec.ts +72 -0
- package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +3 -2
- package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +12 -0
- package/src/chat21-core/providers/scripts/script.service.spec.ts +12 -2
- package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
- package/src/chat21-core/utils/constants.ts +4 -0
- package/src/chat21-core/utils/utils-message.ts +45 -6
- package/src/chat21-core/utils/utils.ts +5 -2
- package/src/widget-config-template.json +4 -1
- package/src/widget-config.json +4 -1
- package/tests/widget-form-rich.spec.ts +67 -0
- package/tests/widget-index-dev-settings.spec.ts +52 -0
- package/tests/widget-twp-iframe.spec.ts +39 -0
- package/tsconfig.json +5 -0
|
@@ -161,6 +161,11 @@ 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
|
+
|
|
164
169
|
@ViewChild(ConversationFooterComponent) conversationFooter: ConversationFooterComponent
|
|
165
170
|
@ViewChild(ConversationContentComponent) conversationContent: ConversationContentComponent
|
|
166
171
|
conversationHandlerService: ConversationHandlerService
|
|
@@ -246,7 +251,22 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
246
251
|
'CONTINUE',
|
|
247
252
|
'EMOJI_NOT_ELLOWED',
|
|
248
253
|
'ATTACHMENT',
|
|
249
|
-
'EMOJI'
|
|
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'
|
|
250
270
|
];
|
|
251
271
|
|
|
252
272
|
const keysContent = [
|
|
@@ -266,13 +286,21 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
266
286
|
'LABEL_THINKING',
|
|
267
287
|
'LABEL_TO',
|
|
268
288
|
'ARRAY_DAYS',
|
|
289
|
+
'CONVERSATION_LOG_LABEL',
|
|
290
|
+
'BUTTON_SCROLL_TO_BOTTOM',
|
|
291
|
+
'CAROUSEL_PREVIOUS',
|
|
292
|
+
'CAROUSEL_NEXT',
|
|
293
|
+
'CAROUSEL_LABEL',
|
|
294
|
+
'CAROUSEL_SLIDE_LABEL'
|
|
269
295
|
];
|
|
270
296
|
|
|
271
297
|
const keysPreview= [
|
|
272
298
|
'BACK',
|
|
273
299
|
'CLOSE',
|
|
274
300
|
'LABEL_PLACEHOLDER',
|
|
275
|
-
'LABEL_PREVIEW'
|
|
301
|
+
'LABEL_PREVIEW',
|
|
302
|
+
'BUTTON_CLOSE_PREVIEW',
|
|
303
|
+
'BUTTON_SEND_MESSAGE'
|
|
276
304
|
];
|
|
277
305
|
|
|
278
306
|
const keysCloseChatDialog= [
|
|
@@ -501,25 +529,29 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
501
529
|
return this.isConversationArchived;
|
|
502
530
|
}
|
|
503
531
|
|
|
504
|
-
//FALLBACK TO TILEDESK
|
|
505
|
-
|
|
532
|
+
// FALLBACK TO TILEDESK
|
|
533
|
+
let requests_list: { requests: any[] };
|
|
534
|
+
try {
|
|
535
|
+
requests_list = await this.tiledeskRequestService.getMyRequests();
|
|
536
|
+
} catch (err) {
|
|
506
537
|
this.logger.error('[CONV-COMP] getConversationDetail: error getting request from Tiledesk', err);
|
|
507
|
-
this.isConversationArchived=true
|
|
508
|
-
return
|
|
509
|
-
}
|
|
538
|
+
this.isConversationArchived = true;
|
|
539
|
+
return this.isConversationArchived;
|
|
540
|
+
}
|
|
541
|
+
|
|
510
542
|
if (requests_list && requests_list.requests.length > 0) {
|
|
511
543
|
this.logger.debug('[CONV-COMP] getConversationDetail: request exist on Tiledesk', requests_list);
|
|
512
|
-
|
|
513
|
-
if(conversation){
|
|
514
|
-
this.isConversationArchived = false
|
|
515
|
-
return this.isConversationArchived
|
|
544
|
+
const conversation = requests_list.requests.find((request) => request.request_id === this.conversationId);
|
|
545
|
+
if (conversation) {
|
|
546
|
+
this.isConversationArchived = false;
|
|
547
|
+
return this.isConversationArchived;
|
|
516
548
|
}
|
|
517
549
|
this.logger.debug('[CONV-COMP] getConversationDetail: request NOT EXIST on Tiledesk', requests_list);
|
|
518
|
-
this.isConversationArchived = true
|
|
519
|
-
return this.isConversationArchived
|
|
550
|
+
this.isConversationArchived = true;
|
|
551
|
+
return this.isConversationArchived;
|
|
520
552
|
}
|
|
521
553
|
|
|
522
|
-
this.isConversationArchived =
|
|
554
|
+
this.isConversationArchived = false;
|
|
523
555
|
return null;
|
|
524
556
|
}
|
|
525
557
|
|
|
@@ -822,6 +854,10 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
822
854
|
this.showThinkingMessage = false;
|
|
823
855
|
}
|
|
824
856
|
|
|
857
|
+
if (this.isStreamAudioActive && msg.sender !== this.senderId) {
|
|
858
|
+
this.conversationFooter?.interruptStreamDueToPeerMessage();
|
|
859
|
+
}
|
|
860
|
+
|
|
825
861
|
that.newMessageAdded(msg);
|
|
826
862
|
// Update badge based on the latest message received from the server.
|
|
827
863
|
// We rely on `messages` being kept in-sync by the conversation handler.
|
|
@@ -877,6 +913,20 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
877
913
|
this.subscriptions.push(subscribe);
|
|
878
914
|
}
|
|
879
915
|
|
|
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
|
+
|
|
880
930
|
subscribtionKey = 'messageWait';
|
|
881
931
|
subscribtion = this.subscriptions.find(item => item.key === subscribtionKey);
|
|
882
932
|
if (!subscribtion) {
|
|
@@ -1032,6 +1082,21 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1032
1082
|
|
|
1033
1083
|
|
|
1034
1084
|
|
|
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
|
+
|
|
1035
1100
|
scrollToBottom() {
|
|
1036
1101
|
this.conversationContent.scrollToBottom();
|
|
1037
1102
|
// const that = this;
|
|
@@ -1383,8 +1448,29 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
1383
1448
|
this.logger.debug('[CONV-COMP] floating onNewConversationButtonClicked')
|
|
1384
1449
|
this.onNewConversationButtonClicked.emit()
|
|
1385
1450
|
}
|
|
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
|
+
}
|
|
1386
1465
|
// =========== END: event emitter function ====== //
|
|
1387
1466
|
|
|
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
|
+
}
|
|
1388
1474
|
|
|
1389
1475
|
openInputFiles() {
|
|
1390
1476
|
alert('ok');
|
|
@@ -1,32 +1,44 @@
|
|
|
1
1
|
<div class="audio-recorder">
|
|
2
|
-
<button *ngIf="audioUrl" (click)="deleteRecording()">
|
|
2
|
+
<button *ngIf="audioUrl" type="button" [attr.aria-label]="translationMap?.get('BUTTON_DELETE_AUDIO') || 'Delete recording'" (click)="deleteRecording()">
|
|
3
3
|
<span class="v-align-center">
|
|
4
|
-
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
|
|
4
|
+
<svg aria-hidden="true" focusable="false" 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> -->
|
|
8
7
|
</span>
|
|
9
8
|
</button>
|
|
10
9
|
|
|
11
|
-
<chat-audio
|
|
12
|
-
[audioBlob]
|
|
10
|
+
<chat-audio class="test" *ngIf="audioBlob && audioUrl"
|
|
11
|
+
[audioBlob]="audioBlob"
|
|
13
12
|
[color]="'var(--chat-footer-color)'"
|
|
13
|
+
[translationMap]="translationMap"
|
|
14
14
|
[stylesMap]="stylesMap">
|
|
15
15
|
</chat-audio>
|
|
16
|
-
|
|
17
|
-
<button *ngIf="!audioUrl"
|
|
18
|
-
|
|
16
|
+
|
|
17
|
+
<button *ngIf="!audioUrl"
|
|
18
|
+
type="button"
|
|
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">
|
|
19
29
|
<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"/>
|
|
20
30
|
</svg>
|
|
21
31
|
</button>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
|
|
33
|
+
<button *ngIf="audioUrl"
|
|
34
|
+
type="button"
|
|
35
|
+
[attr.aria-label]="translationMap?.get('BUTTON_SEND_AUDIO') || 'Send audio message'"
|
|
36
|
+
(click)="sendMessage()">
|
|
25
37
|
<span class="v-align-center">
|
|
26
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
38
|
+
<svg aria-hidden="true" focusable="false" 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">
|
|
27
39
|
<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"/>
|
|
28
40
|
</svg>
|
|
29
41
|
</span>
|
|
30
42
|
</button>
|
|
31
43
|
|
|
32
|
-
</div>
|
|
44
|
+
</div>
|
|
@@ -1,23 +1,141 @@
|
|
|
1
|
-
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
1
|
+
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
|
2
2
|
|
|
3
3
|
import { ConversationAudioRecorderComponent } from './conversation-audio-recorder.component';
|
|
4
4
|
|
|
5
|
-
describe('
|
|
5
|
+
describe('ConversationAudioRecorderComponent', () => {
|
|
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
|
+
};
|
|
8
15
|
|
|
9
16
|
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
|
+
|
|
10
44
|
await TestBed.configureTestingModule({
|
|
11
|
-
declarations: [
|
|
12
|
-
})
|
|
13
|
-
.compileComponents();
|
|
45
|
+
declarations: [ConversationAudioRecorderComponent],
|
|
46
|
+
}).compileComponents();
|
|
14
47
|
|
|
15
48
|
fixture = TestBed.createComponent(ConversationAudioRecorderComponent);
|
|
16
49
|
component = fixture.componentInstance;
|
|
50
|
+
component.translationMap = new Map();
|
|
51
|
+
component.stylesMap = new Map();
|
|
52
|
+
spyOn(component.startRecordingEvent, 'emit');
|
|
53
|
+
spyOn(component.endRecordingEvent, 'emit');
|
|
17
54
|
fixture.detectChanges();
|
|
18
55
|
});
|
|
19
56
|
|
|
20
57
|
it('should create', () => {
|
|
21
58
|
expect(component).toBeTruthy();
|
|
22
59
|
});
|
|
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
|
+
});
|
|
23
141
|
});
|
|
@@ -9,6 +9,7 @@ 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>;
|
|
12
13
|
@Output() startRecordingEvent = new EventEmitter<void>();
|
|
13
14
|
@Output() deleteRecordingEvent = new EventEmitter<void>();
|
|
14
15
|
@Output() endRecordingEvent = new EventEmitter<Blob | null>();
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
<div class="c21-body-container">
|
|
4
4
|
|
|
5
|
-
<div class="c21-body-content"
|
|
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'">
|
|
6
11
|
|
|
7
12
|
<!-- USER TYPING (WAIT MESSAGE) -->
|
|
8
13
|
<span *ngIf="messages && this.messages.length === 0 && !isTypings">
|
|
@@ -19,21 +24,22 @@
|
|
|
19
24
|
<div #scrollMe id="scroll-me" (scroll)="onScroll($event)">
|
|
20
25
|
|
|
21
26
|
<div id="{{idDivScroll}}" class="c21-contentScroll" > <!-- (resized)="onResized($event)" -->
|
|
22
|
-
<div *ngFor="let message of messages; let first = first; let last = last; let i = index"
|
|
23
|
-
|
|
27
|
+
<div *ngFor="let message of messages; let first = first; let last = last; let i = index" class="rowMsg">
|
|
28
|
+
|
|
24
29
|
<!-- message SENDER:: -->
|
|
25
|
-
|
|
30
|
+
<div role="article" *ngIf="messageType(MESSAGE_TYPE_MINE, message) && !message.isJustRecived" class="msg_container base_sent">
|
|
26
31
|
|
|
27
32
|
<!--backgroundColor non viene ancora usato -->
|
|
28
33
|
<!-- class="messages msg_sent slide-in-right" -->
|
|
29
34
|
<chat-bubble-message class="messages msg_sent"
|
|
30
|
-
[class.no-background]="(isImage(message) || isFrame(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
35
|
+
[class.no-background]="(isImage(message) || isFrame(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
31
36
|
[class.emoticon]="isEmojii(message?.text)"
|
|
32
37
|
[ngStyle]="{'background': stylesMap.get('bubbleSentBackground'), 'color': stylesMap.get('bubbleSentTextColor')}"
|
|
33
38
|
[ngClass]="{'button-in-msg' : message?.metadata && message?.metadata?.button}"
|
|
34
39
|
[message]="message"
|
|
35
40
|
[fontColor]="stylesMap.get('bubbleSentTextColor')"
|
|
36
41
|
[stylesMap]="stylesMap"
|
|
42
|
+
[translationMap]="translationMap"
|
|
37
43
|
(onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
|
|
38
44
|
(onAfterMessageRender)="onAfterMessageRenderFN($event)"
|
|
39
45
|
(onElementRendered)="onElementRenderedFN($event)">
|
|
@@ -47,9 +53,9 @@
|
|
|
47
53
|
</div>
|
|
48
54
|
|
|
49
55
|
<!-- message RECIPIENT:: -->
|
|
50
|
-
<div role="
|
|
56
|
+
<div role="article" *ngIf="messageType(MESSAGE_TYPE_OTHERS, message)" class="msg_container base_receive">
|
|
51
57
|
|
|
52
|
-
<chat-avatar-image *ngIf="!isSameSender(message?.sender, i)"
|
|
58
|
+
<chat-avatar-image *ngIf="!isSameSender(message?.sender, i) && !isStreamAudioActive"
|
|
53
59
|
[ngClass]="{'slide-in-left': false}"
|
|
54
60
|
[senderID]="message?.sender"
|
|
55
61
|
[senderFullname]="message?.sender_fullname"
|
|
@@ -60,14 +66,17 @@
|
|
|
60
66
|
<!-- [ngClass]="{'slide-in-left': !isFirstMessage(message?.sender, i)}" -->
|
|
61
67
|
<chat-bubble-message class="messages msg_receive"
|
|
62
68
|
[ngClass]="{'slide-in-left': false}"
|
|
63
|
-
[class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
69
|
+
[class.no-background]="(isImage(message) || isFrame(message) || isCarousel(message)) && ((message?.text && message?.text.trim() === '') || !message?.text)"
|
|
64
70
|
[class.emoticon]="isEmojii(message?.text)"
|
|
65
|
-
[
|
|
71
|
+
[class.fullSizeMessage]="isStreamAudioActive"
|
|
72
|
+
[style.margin-left]="isSameSender(message?.sender, i) ? 'calc(var(--avatar-width) + 10px)' : null"
|
|
66
73
|
[ngStyle]="{'background': stylesMap.get('bubbleReceivedBackground'), 'color': stylesMap.get('bubbleReceivedTextColor'), 'width':isFrame(message) ?'100%' : null}"
|
|
67
74
|
[isSameSender]="isSameSender(message?.sender, i)"
|
|
68
75
|
[message]="message"
|
|
69
76
|
[fontColor]="stylesMap.get('bubbleReceivedTextColor')"
|
|
70
77
|
[stylesMap]="stylesMap"
|
|
78
|
+
[translationMap]="translationMap"
|
|
79
|
+
[streamOnArrival]="false"
|
|
71
80
|
(onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
|
|
72
81
|
(onAfterMessageRender)="onAfterMessageRenderFN($event)"
|
|
73
82
|
(onElementRendered)="onElementRenderedFN($event)">
|
|
@@ -110,6 +119,7 @@
|
|
|
110
119
|
[isConversationArchived]="isConversationArchived"
|
|
111
120
|
[isLastMessage] = "isLastMessage(message?.uid)"
|
|
112
121
|
[stylesMap]="stylesMap"
|
|
122
|
+
[translationMap]="translationMap"
|
|
113
123
|
(onElementRendered)="onElementRenderedFN($event)"
|
|
114
124
|
(onAttachmentButtonClicked)="onAttachmentButtonClickedFN($event)">
|
|
115
125
|
</chat-carousel>
|
|
@@ -134,9 +144,10 @@
|
|
|
134
144
|
[senderFullname]="nameUserTypingNow"
|
|
135
145
|
[baseLocation]="baseLocation">
|
|
136
146
|
</chat-avatar-image>
|
|
147
|
+
|
|
137
148
|
<user-typing
|
|
138
|
-
[ngClass]="{'userTypingNowExist': !idUserTypingNow}"
|
|
139
149
|
[color]="stylesMap?.get('iconColor')"
|
|
150
|
+
[ngClass]="{'userTypingNowExist': !idUserTypingNow}"
|
|
140
151
|
[translationMap]="translationMap"
|
|
141
152
|
[idUserTypingNow]="idUserTypingNow"
|
|
142
153
|
[nameUserTypingNow]="nameUserTypingNow">
|
|
@@ -145,7 +156,9 @@
|
|
|
145
156
|
|
|
146
157
|
<div *ngIf="showThinkingMessage && lastServerSenderKind === 'bot'" class="msg_container base_receive thinking_receive">
|
|
147
158
|
<user-typing class="loading thinking-dots"
|
|
159
|
+
[class.fullSize]="isStreamAudioActive"
|
|
148
160
|
[color]="stylesMap?.get('iconColor')"
|
|
161
|
+
[class.fullSize]="isStreamAudioActive"
|
|
149
162
|
[translationMap]="translationMap"
|
|
150
163
|
[idUserTypingNow]="idUserTypingNow"
|
|
151
164
|
[nameUserTypingNow]="nameUserTypingNow">
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
margin: 25px 50px
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
:host .loading.fullSize ::ng-deep > div.spinner{
|
|
31
|
+
margin: 15px 0px !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
// ============= CSS c21-body ================= //
|
|
31
35
|
.c21-body {
|
|
32
36
|
// -webkit-box-shadow: inset 0 10px 10px -10px rgba(0,0,0,0.4);
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
top: 0;
|
|
45
49
|
right: 0;
|
|
46
50
|
left: 0;
|
|
47
|
-
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
|
|
51
|
+
bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
|
|
48
52
|
overflow: hidden;
|
|
49
53
|
.time{
|
|
50
54
|
margin-bottom: 20px;
|
|
@@ -164,6 +168,12 @@
|
|
|
164
168
|
min-width: 14px;
|
|
165
169
|
border: 0.1px solid #0000000f;
|
|
166
170
|
}
|
|
171
|
+
.msg_sent.json-resources{
|
|
172
|
+
border: 0 !important;
|
|
173
|
+
width: 100%;
|
|
174
|
+
max-width: 652px;
|
|
175
|
+
flex: 1 1 auto;
|
|
176
|
+
}
|
|
167
177
|
.message_innerhtml {
|
|
168
178
|
padding: 8px;
|
|
169
179
|
}
|
|
@@ -236,6 +246,18 @@
|
|
|
236
246
|
height: fit-content;
|
|
237
247
|
width: auto;
|
|
238
248
|
|
|
249
|
+
&.fullSizeMessage {
|
|
250
|
+
max-width: 100%;
|
|
251
|
+
margin: auto 0 auto 0 !important;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
}
|
|
255
|
+
.msg_receive.json-resources{
|
|
256
|
+
min-height: unset;
|
|
257
|
+
padding: 0;
|
|
258
|
+
width: 100%;
|
|
259
|
+
max-width: 652px;
|
|
260
|
+
flex: 1 1 auto;
|
|
239
261
|
}
|
|
240
262
|
|
|
241
263
|
|
|
@@ -270,6 +292,15 @@
|
|
|
270
292
|
}// end c21-body-container
|
|
271
293
|
}// end c21-body
|
|
272
294
|
|
|
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
|
+
|
|
273
304
|
@keyframes thinking-dot {
|
|
274
305
|
0%, 80%, 100% {
|
|
275
306
|
opacity: 0.2;
|
|
@@ -332,4 +363,4 @@
|
|
|
332
363
|
}
|
|
333
364
|
}
|
|
334
365
|
|
|
335
|
-
// ============= END CSS c21-body ================= //
|
|
366
|
+
// ============= END CSS c21-body ================= //
|