@chat21/chat21-web-widget 5.0.86 → 5.0.89-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/package.json +1 -1
  3. package/src/app/app.module.ts +5 -2
  4. package/src/app/component/conversation-detail/conversation/conversation.component.ts +16 -16
  5. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +29 -0
  6. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.scss +103 -0
  7. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +23 -0
  8. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +96 -0
  9. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +14 -3
  10. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +26 -10
  11. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +168 -77
  12. package/src/app/component/message/audio-track/audio-track.component.html +32 -0
  13. package/src/app/component/message/audio-track/audio-track.component.scss +107 -0
  14. package/src/app/component/message/{audio/audio.component.spec.ts → audio-track/audio-track.component.spec.ts} +6 -6
  15. package/src/app/component/message/audio-track/audio-track.component.ts +147 -0
  16. package/src/app/component/message/bubble-message/bubble-message.component.html +31 -15
  17. package/src/app/component/message/bubble-message/bubble-message.component.scss +7 -0
  18. package/src/app/component/message/bubble-message/bubble-message.component.ts +1 -0
  19. package/src/app/utils/globals.ts +1 -1
  20. package/src/app/utils/utils.ts +45 -0
  21. package/src/assets/twp/blank.html +3 -6
  22. package/src/assets/twp/chatbot-panel.html +5 -13
  23. package/src/assets/twp/index-dev.html +8 -15
  24. package/src/assets/twp/index.html +7 -13
  25. package/src/assets/twp/tiledesk_widget_files/bootstrap.min.css +4 -3
  26. package/src/assets/twp/tiledesk_widget_files/bootstrap.min.js +3 -4
  27. package/src/assets/twp/tiledesk_widget_files/jquery.min.js +2 -2
  28. package/src/app/component/message/audio/audio.component.html +0 -20
  29. package/src/app/component/message/audio/audio.component.scss +0 -122
  30. package/src/app/component/message/audio/audio.component.ts +0 -122
package/CHANGELOG.md CHANGED
@@ -6,10 +6,31 @@
6
6
  ### **Copyrigth**:
7
7
  *Tiledesk SRL*
8
8
 
9
- # 5.0.86 in PROD
9
+ # 5.0.89-rc.1
10
+ - **added**: remove recipientId from storage after conversation is closed
11
+
12
+ # 5.0.85-rc.2
13
+ - **added**: loading progress indicator while closing a conversation
14
+
15
+ # 5.0.85-rc.1
16
+ - **added**: hide header restart menu option if conversation is closed
17
+ - **removed**: continueConversationBeforeTime settings property
10
18
 
11
19
  # 5.0.85 in PROD
12
20
 
21
+ # 5.0.84-rc.4
22
+ - **added**: checkAcceptedFile fn on dragleave event conversation-content component
23
+ - **added**: checkAcceptedFile fn in conversation-footer component
24
+
25
+ # 5.0.84-rc.3
26
+ - **changed**: index-dev lib order import
27
+
28
+ # 5.0.84-rc.2
29
+ - **changed**: bootstrap, jquery and font-awesome libs
30
+
31
+ # 5.0.84-rc.1
32
+ - **removed**: innerHtml from document element
33
+
13
34
  # 5.0.83 in PROD
14
35
 
15
36
  # 5.0.83-rc.4
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-web-widget",
3
3
  "author": "Tiledesk SRL",
4
- "version": "5.0.86",
4
+ "version": "5.0.89-rc.1",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.tiledesk.com",
7
7
  "repository": {
@@ -15,6 +15,7 @@ import { ConversationContentComponent } from './component/conversation-detail/co
15
15
  import { ConversationFooterComponent } from './component/conversation-detail/conversation-footer/conversation-footer.component';
16
16
  import { ConversationInternalFrameComponent } from './component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component';
17
17
  import { ConversationPreviewComponent } from './component/conversation-detail/conversation-preview/conversation-preview.component';
18
+ import { ConversationAudioRecorderComponent } from './component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component';
18
19
  /** CONVERSATION-DETAIL COMPONENTS */
19
20
  import { BubbleMessageComponent } from './component/message/bubble-message/bubble-message.component';
20
21
  import { AvatarComponent } from './component/message/avatar/avatar.component';
@@ -23,7 +24,7 @@ import { ImageComponent } from './component/message/image/image.component';
23
24
  import { InfoMessageComponent } from './component/message/info-message/info-message.component';
24
25
  import { HtmlComponent } from './component/message/html/html.component';
25
26
  import { FrameComponent } from './component/message/frame/frame.component';
26
- import { AudioComponent } from './component/message/audio/audio.component';
27
+ import { AudioTrackComponent } from './component/message/audio-track/audio-track.component';
27
28
  import { UserTypingComponent } from './../chat21-core/utils/user-typing/user-typing.component';
28
29
  /** MESSAGE ATTACHMENTS COMPONENTS */
29
30
  import { MessageAttachmentComponent } from './component/message-attachment/message-attachment.component';
@@ -138,6 +139,7 @@ import { ConfirmCloseComponent } from './modals/confirm-close/confirm-close.comp
138
139
 
139
140
 
140
141
 
142
+
141
143
  const appInitializerFn = (appConfig: AppConfigService, brandService: BrandService, logger: NGXLogger) => {
142
144
  return async() => {
143
145
  let customLogger = new CustomLogger(logger)
@@ -276,6 +278,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
276
278
  ConversationFooterComponent,
277
279
  ConversationPreviewComponent,
278
280
  ConversationInternalFrameComponent,
281
+ ConversationAudioRecorderComponent,
279
282
  BubbleMessageComponent,
280
283
  AvatarComponent,
281
284
  FrameComponent,
@@ -287,6 +290,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
287
290
  ActionButtonComponent,
288
291
  LinkButtonComponent,
289
292
  TextButtonComponent,
293
+ AudioTrackComponent,
290
294
  UserTypingComponent,
291
295
  /**DIRECTIVES */
292
296
  HtmlEntitiesEncodePipe,
@@ -295,7 +299,6 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
295
299
  SafeHtmlPipe,
296
300
  LikeUnlikeComponent,
297
301
  TooltipDirective,
298
- AudioComponent,
299
302
  CarouselComponent,
300
303
  NetworkOfflineComponent,
301
304
  ConfirmCloseComponent
@@ -39,6 +39,7 @@ import { CustomTranslateService } from 'src/chat21-core/providers/custom-transla
39
39
  import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
40
40
  import { TiledeskRequestsService } from 'src/chat21-core/providers/tiledesk/tiledesk-requests.service';
41
41
  import { ConversationContentComponent } from '../conversation-content/conversation-content.component';
42
+ import { checkAcceptedFile } from 'src/app/utils/utils';
42
43
  // import { TranslateService } from '@ngx-translate/core';
43
44
 
44
45
  @Component({
@@ -1002,7 +1003,8 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1002
1003
  case 'confirm':
1003
1004
  this.tiledeskRequestService.closeSupportGroup(this.conversationId).then(data => {
1004
1005
  if(data === 'closed'){
1005
- this.mydialog.nativeElement.close()
1006
+ this.mydialog.nativeElement.close();
1007
+ this.appStorageService.removeItem('recipientId');
1006
1008
  this.logger.debug('[CONV-COMP] chat closed successfully with uid ', this.conversationId)
1007
1009
  }
1008
1010
  }).catch(error => {
@@ -1346,34 +1348,32 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
1346
1348
  var mimeType = fileList[0].type
1347
1349
  this.logger.log('[CONV-COMP] ----> FILE - DROP mimeType files ', mimeType)
1348
1350
 
1349
- // if (mimeType.startsWith("image") || mimeType.startsWith("application")) {
1350
- // this.logger.log('[CONV-COMP] ----> FILE - DROP mimeType files: ', this.appConfigProvider.getConfig().fileUploadAccept);
1351
- // this.checkAcceptedFile(mimeType);
1352
- // const isAccepted = this.checkAcceptedFile(mimeType)
1353
- // this.logger.log('[CONV-COMP] > checkAcceptedFile - fileUploadAccept isAcceptFile FILE - DROP',isAccepted)
1354
- // if (isAccepted === true) {
1355
1351
  this.dropEvent = event
1356
- // } else {
1357
- // this.logger.log( '[CONV-COMP] ----> FILE - DROP mimeType files ', mimeType,'NOT SUPPORTED FILE TYPE')
1358
- // this.presentToast(this.translationsMap.get('FAILED_TO_UPLOAD_THE_FORMAT_IS_NOT_SUPPORTED'), 'danger','toast-custom-class', 5000 )
1359
- // // this.presentToastOnlyImageFilesAreAllowedToDrag()
1360
- // }
1361
1352
 
1362
1353
  }
1363
1354
  }
1364
1355
 
1365
1356
  allowDrop(event: any) {
1366
- event.preventDefault()
1367
- event.stopPropagation()
1357
+ event.preventDefault();
1358
+ event.stopPropagation();
1368
1359
  this.logger.log('[CONV-COMP] ----> FILE - (dragover) allowDrop ev ', event)
1369
1360
  this.isHovering = true
1370
1361
  }
1371
1362
 
1372
1363
  drag(event){
1373
- event.preventDefault()
1364
+ event.preventDefault();
1365
+ this.logger.log('[CONV-COMP] ----> FILE - (dragleave) drag ev ', event)
1366
+ if (event.dataTransfer && event.dataTransfer.files) {
1367
+ const files = event.dataTransfer.files;
1368
+ const canUploadFile = checkAcceptedFile(files[0].type, this.appConfigService.getConfig().fileUploadAccept)
1369
+ if(!canUploadFile){
1370
+ this.logger.error('[IMAGE-UPLOAD] detectFiles: can not upload current file type--> NOT ALLOWED', this.appConfigService.getConfig().fileUploadAccept)
1371
+ return;
1372
+ }
1373
+ }
1374
1374
  event.stopPropagation()
1375
- console.log('dragleave-->', event)
1376
1375
  this.isHovering = false
1377
1376
  }
1378
1377
 
1379
1378
  }
1379
+
@@ -0,0 +1,29 @@
1
+ <div class="audio-recorder">
2
+ <button *ngIf="audioUrl" (click)="deleteRecording()">
3
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px"><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"/></svg>
4
+ <!-- <i class="material-icons">delete_outline</i> -->
5
+ </button>
6
+
7
+ <chat-audio-track *ngIf="audioBlob && audioUrl"
8
+ [audioBlob] = "audioBlob"
9
+ [color]="stylesMap.get('themeColor')"
10
+ [fontSize]="stylesMap.get('fontSize')"
11
+ [stylesMap]="stylesMap">
12
+ </chat-audio-track>
13
+
14
+ <button *ngIf="!audioUrl" class="mic-button" (mousedown)="startRecording()" (mouseup)="stopRecording()">
15
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px">
16
+ <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"/>
17
+ </svg>
18
+ </button>
19
+ <!-- <button *ngIf="isRecording" (click)="stopRecording()"><i class="material-icons">pause_circle_outline</i></button> -->
20
+
21
+ <button *ngIf="audioUrl" (click)="sendMessage()">
22
+ <span class="v-align-center">
23
+ <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">
24
+ <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"/>
25
+ </svg>
26
+ </span>
27
+ </button>
28
+
29
+ </div>
@@ -0,0 +1,103 @@
1
+
2
+ @font-face {
3
+ font-family: 'Material Icons';
4
+ src: url('https://fonts.googleapis.com/icon?family=Material+Icons');
5
+ }
6
+
7
+ .material-icons.mic_none::before {
8
+ content: 'mic_none';
9
+ }
10
+
11
+ .audio-recorder {
12
+ text-align: center;
13
+ margin: 0px;
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ height: 100%;
18
+ width: 100%;
19
+ float: left;
20
+ gap: 10px
21
+ }
22
+
23
+ chat-audio-track {
24
+ display: flex;
25
+ width: 70%;
26
+ margin: 8px 0;
27
+ pointer-events: auto;
28
+ border-radius: var(--chat-footer-border-radius);
29
+ background-color: var(--chat-footer-background-color);
30
+ }
31
+
32
+ button {
33
+ margin: 0px;
34
+ padding: 0px;
35
+ font-size: 16px;
36
+ border: none;
37
+ background-color: transparent;
38
+ color: var(--icon-fill-color);
39
+ fill: var(--icon-fill-color);
40
+ height: var(--chat-footer-height);
41
+ }
42
+
43
+ .mic-button {
44
+ position: relative;
45
+ background-color: transparent;
46
+ border: none;
47
+ outline: none;
48
+ cursor: pointer;
49
+ padding: 20px;
50
+ border-radius: 50%;
51
+ overflow: hidden;
52
+ display: inline-flex;
53
+ justify-content: center;
54
+ align-items: center;
55
+ }
56
+
57
+ .mic-button svg {
58
+ width: 24px;
59
+ height: 24px;
60
+ fill: var(--icon-fill-color);
61
+ }
62
+
63
+ .mic-button::before {
64
+ content: '';
65
+ position: absolute;
66
+ top: auto;
67
+ left: auto;
68
+ width: 35px;
69
+ height: 35px;
70
+ background-color: var(--icon-fill-color);
71
+ border-radius: 50%;
72
+ transform: scale(0);
73
+ transition: transform 0.3s ease;
74
+ transform-origin: center;
75
+ z-index: -1;
76
+ }
77
+
78
+ .mic-button:active::before {
79
+ transform: scale(0.5);
80
+ animation: pulse 1s infinite ease-in-out;
81
+ }
82
+
83
+ .mic-button:active {
84
+ svg {
85
+ color: #fff;
86
+ fill: #fff;
87
+ }
88
+ }
89
+
90
+ @keyframes pulse {
91
+ 0% {
92
+ transform: scale(1);
93
+ opacity: 1;
94
+ }
95
+ 50% {
96
+ transform: scale(0.9);
97
+ opacity: 0.7;
98
+ }
99
+ 100% {
100
+ transform: scale(1);
101
+ opacity: 1;
102
+ }
103
+ }
@@ -0,0 +1,23 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { ConversationAudioRecorderComponent } from './conversation-audio-recorder.component';
4
+
5
+ describe('AudioRecorderComponent', () => {
6
+ let component: ConversationAudioRecorderComponent;
7
+ let fixture: ComponentFixture<ConversationAudioRecorderComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ declarations: [ ConversationAudioRecorderComponent ]
12
+ })
13
+ .compileComponents();
14
+
15
+ fixture = TestBed.createComponent(ConversationAudioRecorderComponent);
16
+ component = fixture.componentInstance;
17
+ fixture.detectChanges();
18
+ });
19
+
20
+ it('should create', () => {
21
+ expect(component).toBeTruthy();
22
+ });
23
+ });
@@ -0,0 +1,96 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
3
+
4
+ @Component({
5
+ selector: 'chat-audio-recorder',
6
+ templateUrl: './conversation-audio-recorder.component.html',
7
+ styleUrls: ['./conversation-audio-recorder.component.scss']
8
+ })
9
+ export class ConversationAudioRecorderComponent {
10
+
11
+ @Input() stylesMap: Map<string, string>;
12
+ @Output() startRecordingEvent = new EventEmitter<void>();
13
+ @Output() deleteRecordingEvent = new EventEmitter<void>();
14
+ @Output() endRecordingEvent = new EventEmitter<Blob | null>();
15
+ @Output() sendRecordingEvent = new EventEmitter<Blob | null>();
16
+
17
+ mediaRecorder: MediaRecorder | null = null;
18
+ audioChunks: Blob[] = [];
19
+ audioBlob: Blob | null = null;
20
+ audioUrl: SafeUrl | null = null;
21
+
22
+ isRecording = false;
23
+ rawAudioUrl: string | null = null;
24
+ isPlaying: boolean = false;
25
+ startTime: number;
26
+
27
+
28
+
29
+ constructor(private sanitizer: DomSanitizer) {}
30
+
31
+
32
+ startRecording() {
33
+ // console.log('startRecording');
34
+ this.startTime = Date.now();
35
+ navigator.mediaDevices.getUserMedia({ audio: true })
36
+ .then(stream => {
37
+ this.mediaRecorder = new MediaRecorder(stream);
38
+ this.mediaRecorder.start();
39
+ this.isRecording = true;
40
+ this.startRecordingEvent.emit();
41
+ this.mediaRecorder.addEventListener('dataavailable', (event) => {
42
+ this.audioChunks.push(event.data);
43
+ });
44
+
45
+ this.mediaRecorder.addEventListener('stop', () => {
46
+ this.audioBlob = new Blob(this.audioChunks, { type: 'audio/mpeg' });
47
+ this.rawAudioUrl = URL.createObjectURL(this.audioBlob);
48
+ this.audioUrl = this.sanitizer.bypassSecurityTrustUrl(this.rawAudioUrl);
49
+ this.audioChunks = [];
50
+ this.endRecordingEvent.emit(this.audioBlob);
51
+ });
52
+ })
53
+ .catch(error => {
54
+ console.error('Errore nell’accesso al microfono:', error);
55
+ });
56
+ }
57
+
58
+ stopRecording() {
59
+ let endTime = Date.now();
60
+ let time = endTime - this.startTime;
61
+ if(time > 500){
62
+ setTimeout(() => {
63
+ if (this.mediaRecorder) {
64
+ this.mediaRecorder.stop();
65
+ this.isRecording = false;
66
+ }
67
+ }, 300);
68
+
69
+ } else {
70
+ this.audioUrl = null;
71
+ this.isPlaying = false;
72
+ this.rawAudioUrl = null;
73
+ this.audioUrl = null;
74
+ this.audioBlob = null;
75
+ }
76
+
77
+
78
+ }
79
+
80
+ deleteRecording() {
81
+ this.audioUrl = null;
82
+ this.isPlaying = false;
83
+ this.rawAudioUrl = null;
84
+ this.audioUrl = null;
85
+ this.audioBlob = null;
86
+ this.deleteRecordingEvent.emit(null);
87
+ }
88
+
89
+ sendMessage(){
90
+ if (this.audioUrl) {
91
+ this.sendRecordingEvent.emit(this.audioBlob);
92
+ this.audioUrl = null;
93
+ }
94
+ }
95
+
96
+ }
@@ -7,7 +7,7 @@
7
7
  <!-- TEXTAREA + ICONS: conv active-->
8
8
  <div class="textarea-container" *ngIf="!hideTextAreaContent && !hideTextReply">
9
9
 
10
- <div class="icons-container">
10
+ <div *ngIf="!isStopRec" class="icons-container">
11
11
  <!-- ICON ATTACHMENT -->
12
12
  <label *ngIf="showAttachmentButton == true" tabindex="1502" aria-label="allegati" for="chat21-file" class="chat21-textarea-button" [class.active]="!isFilePendingToUpload && !hideTextReply" id="chat21-start-upload-doc">
13
13
  <span class="v-align-center">
@@ -44,7 +44,7 @@
44
44
 
45
45
 
46
46
 
47
- <div class="visible-text-area" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
47
+ <div *ngIf="!isStopRec" class="visible-text-area" [class.disabled] = "( isConversationArchived || hideTextReply)? true : null">
48
48
  <!-- isFilePendingToUpload || -->
49
49
  <textarea
50
50
  [attr.disabled] = "(hideTextReply)? true : null"
@@ -67,13 +67,24 @@
67
67
  </div>
68
68
 
69
69
  <!-- ICON SEND -->
70
- <div tabindex="-1" class="chat21-textarea-button" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
70
+ <div *ngIf="textInputTextArea && !isStopRec" tabindex="-1" class="chat21-textarea-button" [class.active]="textInputTextArea && !hideTextReply" id="chat21-button-send" (click)="onSendPressed($event)">
71
71
  <span class="v-align-center">
72
72
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="24" width="24" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
73
73
  <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"/>
74
74
  </svg>
75
75
  </span>
76
76
  </div>
77
+
78
+ <!-- ICON REC -->
79
+ <div *ngIf="!textInputTextArea" tabindex="-1" class="chat21-audio-button" [class.active]="isStopRec" id="chat21-button-rec">
80
+ <chat-audio-recorder
81
+ (startRecordingEvent)="onStartRecording()"
82
+ (deleteRecordingEvent)="onDeleteRecording()"
83
+ (endRecordingEvent)="onEndRecording($event)"
84
+ (sendRecordingEvent)="onSendRecording($event)"
85
+ [stylesMap]="stylesMap">
86
+ </chat-audio-recorder>
87
+ </div>
77
88
  </div>
78
89
 
79
90
 
@@ -7,14 +7,16 @@
7
7
  display: flex;
8
8
  width: 100%;
9
9
  align-items: center;
10
- gap: 10px;
10
+ gap: 0px;
11
11
  justify-content: space-between;
12
12
  }
13
13
 
14
14
  .icons-container{
15
15
  display: flex;
16
16
  align-self: flex-end;
17
- margin-left: 8px
17
+ margin-left: 8px;
18
+ gap: 2px;
19
+ margin-right: 8px;
18
20
  }
19
21
 
20
22
  .visible-text-area {
@@ -31,7 +33,6 @@
31
33
  .chat21-textarea-button {
32
34
  fill: var(--icon-fill-color);
33
35
  pointer-events: auto;
34
-
35
36
  height: var(--chat-footer-height);
36
37
  bottom: 22px;
37
38
  opacity: 0.3;
@@ -185,11 +186,29 @@ textarea:active{
185
186
  }
186
187
 
187
188
  #chat21-button-send {
188
- // right: 8px;
189
- // bottom: 0;
190
189
  display: flex;
191
190
  align-self: flex-end;
192
- margin: 0 8px
191
+ margin: 0 8px;
192
+ width: 34px;
193
+ text-align: center;
194
+ justify-content: center;
195
+ }
196
+
197
+ #chat21-button-rec {
198
+ display: flex;
199
+ width: 34px;
200
+ text-align: center;
201
+ justify-content: center;
202
+ height: var(--chat-footer-height);
203
+ &.active{
204
+ width: 100%;
205
+ }
206
+ chat-audio-recorder {
207
+ width: 100%;
208
+ display: flex;
209
+ align-items: center;
210
+ justify-content: center;
211
+ }
193
212
  }
194
213
 
195
214
  #chat21-file{
@@ -337,7 +356,4 @@ textarea:active{
337
356
  // left: 10px;
338
357
  border: none;
339
358
  margin: -2px -2px 0px;
340
-
341
- }
342
-
343
-
359
+ }