@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.
Files changed (201) hide show
  1. package/.angular-mcp-cache/package.json +1 -0
  2. package/.cursor/angular18-accessibility-auditor-skill.md +442 -0
  3. package/.cursor/mcp.json +15 -0
  4. package/.github/workflows/docker-community-push-latest.yml +23 -13
  5. package/.github/workflows/docker-image-tag-community-tag-push.yml +22 -12
  6. package/.github/workflows/playwright.yml +27 -0
  7. package/CHANGELOG.md +130 -6
  8. package/Dockerfile +4 -5
  9. package/angular.json +24 -4
  10. package/docs/changelog/this-branch.md +36 -0
  11. package/env.sample +3 -2
  12. package/mocks/voice-websocket-mock/server.cjs +245 -0
  13. package/nginx.conf +22 -2
  14. package/package.json +10 -3
  15. package/playwright.config.ts +41 -0
  16. package/src/app/app.component.html +2 -2
  17. package/src/app/app.component.scss +25 -14
  18. package/src/app/app.component.spec.ts +21 -6
  19. package/src/app/app.component.ts +10 -9
  20. package/src/app/app.module.ts +15 -0
  21. package/src/app/component/conversation-detail/conversation/conversation.component.html +25 -11
  22. package/src/app/component/conversation-detail/conversation/conversation.component.scss +40 -2
  23. package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +644 -75
  24. package/src/app/component/conversation-detail/conversation/conversation.component.ts +100 -14
  25. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +25 -13
  26. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +123 -5
  27. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +1 -0
  28. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +23 -10
  29. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +33 -2
  30. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +242 -149
  31. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +8 -6
  32. package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +53 -3
  33. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +200 -96
  34. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +211 -6
  35. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +452 -78
  36. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +291 -76
  37. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +113 -53
  38. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +12 -4
  39. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +274 -29
  40. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +23 -9
  41. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +80 -8
  42. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +29 -23
  43. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +185 -16
  44. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +34 -14
  45. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +46 -0
  46. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +83 -0
  47. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +192 -0
  48. package/src/app/component/error-alert/error-alert.component.spec.ts +65 -5
  49. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +16 -7
  50. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +21 -0
  51. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +89 -7
  52. package/src/app/component/form/form-builder/form-builder.component.html +1 -1
  53. package/src/app/component/form/form-builder/form-builder.component.spec.ts +163 -21
  54. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +8 -4
  55. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +10 -5
  56. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +90 -16
  57. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +26 -0
  58. package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +45 -11
  59. package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +24 -6
  60. package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +14 -5
  61. package/src/app/component/form/inputs/form-text/form-text.component.html +14 -12
  62. package/src/app/component/form/inputs/form-text/form-text.component.scss +11 -1
  63. package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +113 -17
  64. package/src/app/component/form/inputs/form-text/form-text.component.ts +35 -3
  65. package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +13 -11
  66. package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +6 -5
  67. package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +149 -13
  68. package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +26 -0
  69. package/src/app/component/form/prechat-form/prechat-form.component.html +14 -11
  70. package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +102 -10
  71. package/src/app/component/form/prechat-form/prechat-form.component.ts +8 -1
  72. package/src/app/component/form/prechat-form-test-mock.ts +35 -0
  73. package/src/app/component/home/home.component.html +38 -31
  74. package/src/app/component/home/home.component.scss +4 -2
  75. package/src/app/component/home/home.component.spec.ts +226 -11
  76. package/src/app/component/home-conversations/home-conversations.component.html +30 -26
  77. package/src/app/component/home-conversations/home-conversations.component.scss +3 -0
  78. package/src/app/component/home-conversations/home-conversations.component.spec.ts +212 -36
  79. package/src/app/component/last-message/last-message.component.html +15 -9
  80. package/src/app/component/last-message/last-message.component.scss +16 -2
  81. package/src/app/component/last-message/last-message.component.spec.ts +204 -23
  82. package/src/app/component/last-message/last-message.component.ts +4 -1
  83. package/src/app/component/launcher-button/launcher-button.component.html +8 -13
  84. package/src/app/component/launcher-button/launcher-button.component.spec.ts +104 -8
  85. package/src/app/component/list-all-conversations/list-all-conversations.component.html +12 -17
  86. package/src/app/component/list-all-conversations/list-all-conversations.component.scss +2 -0
  87. package/src/app/component/list-conversations/list-conversations.component.html +22 -22
  88. package/src/app/component/menu-options/menu-options.component.html +30 -20
  89. package/src/app/component/menu-options/menu-options.component.spec.ts +125 -9
  90. package/src/app/component/message/audio/audio.component.html +13 -15
  91. package/src/app/component/message/audio/audio.component.spec.ts +140 -5
  92. package/src/app/component/message/audio/audio.component.ts +1 -5
  93. package/src/app/component/message/audio-sync/audio-sync.component.html +18 -0
  94. package/src/app/component/message/audio-sync/audio-sync.component.scss +65 -0
  95. package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +103 -0
  96. package/src/app/component/message/audio-sync/audio-sync.component.ts +643 -0
  97. package/src/app/component/message/avatar/avatar.component.html +2 -2
  98. package/src/app/component/message/avatar/avatar.component.spec.ts +99 -7
  99. package/src/app/component/message/bubble-message/bubble-message.component.html +43 -51
  100. package/src/app/component/message/bubble-message/bubble-message.component.scss +59 -1
  101. package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +154 -57
  102. package/src/app/component/message/bubble-message/bubble-message.component.ts +152 -109
  103. package/src/app/component/message/buttons/action-button/action-button.component.html +3 -4
  104. package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +49 -5
  105. package/src/app/component/message/buttons/link-button/link-button.component.scss +5 -8
  106. package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +50 -5
  107. package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +44 -5
  108. package/src/app/component/message/carousel/carousel.component.html +29 -16
  109. package/src/app/component/message/carousel/carousel.component.scss +20 -8
  110. package/src/app/component/message/carousel/carousel.component.spec.ts +80 -3
  111. package/src/app/component/message/carousel/carousel.component.ts +16 -0
  112. package/src/app/component/message/frame/frame.component.html +9 -4
  113. package/src/app/component/message/frame/frame.component.spec.ts +34 -15
  114. package/src/app/component/message/frame/frame.component.ts +7 -2
  115. package/src/app/component/message/html/html.component.html +1 -1
  116. package/src/app/component/message/html/html.component.scss +1 -1
  117. package/src/app/component/message/html/html.component.spec.ts +24 -7
  118. package/src/app/component/message/image/image.component.html +12 -10
  119. package/src/app/component/message/image/image.component.scss +16 -0
  120. package/src/app/component/message/image/image.component.spec.ts +101 -15
  121. package/src/app/component/message/image/image.component.ts +90 -51
  122. package/src/app/component/message/info-message/info-message.component.spec.ts +26 -14
  123. package/src/app/component/message/json-sources/json-sources.component.html +38 -0
  124. package/src/app/component/message/json-sources/json-sources.component.scss +201 -0
  125. package/src/app/component/message/json-sources/json-sources.component.ts +89 -0
  126. package/src/app/component/message/like-unlike/like-unlike.component.html +7 -9
  127. package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +31 -3
  128. package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +38 -17
  129. package/src/app/component/message/text/text.component.html +3 -3
  130. package/src/app/component/message/text/text.component.scss +80 -86
  131. package/src/app/component/message/text/text.component.spec.ts +106 -13
  132. package/src/app/component/message-attachment/message-attachment.component.spec.ts +134 -13
  133. package/src/app/component/selection-department/selection-department.component.html +21 -23
  134. package/src/app/component/selection-department/selection-department.component.spec.ts +159 -14
  135. package/src/app/component/selection-department/selection-department.component.ts +8 -1
  136. package/src/app/component/send-button/send-button.component.html +5 -13
  137. package/src/app/component/send-button/send-button.component.spec.ts +2 -2
  138. package/src/app/component/star-rating-widget/star-rating-widget.component.html +51 -81
  139. package/src/app/directives/tooltip.directive.spec.ts +8 -4
  140. package/src/app/modals/confirm-close/confirm-close.component.html +20 -8
  141. package/src/app/modals/confirm-close/confirm-close.component.scss +3 -0
  142. package/src/app/modals/confirm-close/confirm-close.component.spec.ts +13 -4
  143. package/src/app/modals/confirm-close/confirm-close.component.ts +8 -1
  144. package/src/app/pipe/html-entites-encode.pipe.spec.ts +35 -2
  145. package/src/app/pipe/marked.pipe.spec.ts +38 -2
  146. package/src/app/pipe/marked.pipe.ts +51 -41
  147. package/src/app/providers/app-config.service.ts +4 -2
  148. package/src/app/providers/brand.service.spec.ts +23 -2
  149. package/src/app/providers/brand.service.ts +1 -1
  150. package/src/app/providers/global-settings.service.spec.ts +1009 -14
  151. package/src/app/providers/global-settings.service.ts +82 -2
  152. package/src/app/providers/json-sources-parser.service.ts +175 -0
  153. package/src/app/providers/translator.service.ts +26 -6
  154. package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +117 -0
  155. package/src/app/providers/tts-audio-playback-coordinator.service.ts +109 -0
  156. package/src/app/providers/url-preview.service.ts +82 -0
  157. package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +12 -0
  158. package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +171 -0
  159. package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +39 -0
  160. package/src/app/providers/voice/audio.types.ts +40 -0
  161. package/src/app/providers/voice/vad.service.spec.ts +28 -0
  162. package/src/app/providers/voice/vad.service.ts +70 -0
  163. package/src/app/providers/voice/voice-streaming.service.spec.ts +23 -0
  164. package/src/app/providers/voice/voice-streaming.service.ts +702 -0
  165. package/src/app/providers/voice/voice-streaming.types.ts +112 -0
  166. package/src/app/providers/voice/voice.service.spec.ts +227 -0
  167. package/src/app/providers/voice/voice.service.ts +973 -0
  168. package/src/app/sass/_variables.scss +3 -0
  169. package/src/app/sass/animations.scss +19 -1
  170. package/src/app/shims/onnxruntime-web-wasm.ts +4 -0
  171. package/src/app/utils/globals.ts +21 -1
  172. package/src/app/utils/json-sources-utils.ts +27 -0
  173. package/src/app/utils/url-utils.ts +98 -0
  174. package/src/app/utils/utils-resources.ts +1 -1
  175. package/src/assets/i18n/en.json +106 -99
  176. package/src/assets/i18n/es.json +107 -100
  177. package/src/assets/i18n/fr.json +107 -100
  178. package/src/assets/i18n/it.json +107 -98
  179. package/src/assets/onnx/ort-wasm-simd-threaded.mjs +59 -0
  180. package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
  181. package/src/assets/sounds/keyboard.mp3 +0 -0
  182. package/src/assets/twp/chatbot-panel.html +3 -1
  183. package/src/assets/twp/index-dev.html +18 -0
  184. package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +14 -0
  185. package/src/assets/vad/silero_vad_legacy.onnx +0 -0
  186. package/src/assets/vad/vad.worklet.bundle.min.js +1 -0
  187. package/src/chat21-core/models/message.ts +2 -1
  188. package/src/chat21-core/providers/chat-manager.spec.ts +72 -0
  189. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +3 -2
  190. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +12 -0
  191. package/src/chat21-core/providers/scripts/script.service.spec.ts +12 -2
  192. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  193. package/src/chat21-core/utils/constants.ts +4 -0
  194. package/src/chat21-core/utils/utils-message.ts +45 -6
  195. package/src/chat21-core/utils/utils.ts +5 -2
  196. package/src/widget-config-template.json +4 -1
  197. package/src/widget-config.json +4 -1
  198. package/tests/widget-form-rich.spec.ts +67 -0
  199. package/tests/widget-index-dev-settings.spec.ts +52 -0
  200. package/tests/widget-twp-iframe.spec.ts +39 -0
  201. package/tsconfig.json +5 -0
@@ -0,0 +1,41 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ /**
4
+ * See https://playwright.dev/docs/test-configuration.
5
+ */
6
+ export default defineConfig({
7
+ testDir: './tests',
8
+ timeout: 120_000,
9
+ expect: { timeout: 30_000 },
10
+ fullyParallel: true,
11
+ forbidOnly: !!process.env.CI,
12
+ retries: process.env.CI ? 2 : 0,
13
+ workers: process.env.CI ? 1 : undefined,
14
+ reporter: 'html',
15
+ use: {
16
+ baseURL: 'http://127.0.0.1:4203',
17
+ trace: 'on-first-retry',
18
+ },
19
+
20
+ /**
21
+ * Solo Chromium: i test TWP chiamano API Tiledesk reali; più browser in parallelo
22
+ * aumentano flakiness. Per Firefox/WebKit decommentare i progetti sotto.
23
+ */
24
+ projects: [
25
+ {
26
+ name: 'chromium',
27
+ use: { ...devices['Desktop Chrome'] },
28
+ },
29
+ // { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
30
+ // { name: 'webkit', use: { ...devices['Desktop Safari'] } },
31
+ ],
32
+
33
+ webServer: {
34
+ command: 'npx ng serve widget --port 4203 --host 127.0.0.1',
35
+ url: 'http://127.0.0.1:4203/',
36
+ reuseExistingServer: !process.env.CI,
37
+ timeout: 300_000,
38
+ stdout: 'pipe',
39
+ stderr: 'pipe',
40
+ },
41
+ });
@@ -1,5 +1,5 @@
1
-
2
- <div id="tiledesk-container" [class.active]="g.isShown" tabindex="9999">
1
+ <!-- tabindex="9999" -->
2
+ <div id="tiledesk-container" [class.active]="g.isShown">
3
3
 
4
4
  <div id="chat21-conversations"
5
5
  [ngClass]="{ 'swing-in-bottom-fwd' : g.isOpen === true, 'full-screen-mode' : g.fullscreenMode === true, 'c21-align-left' : g.align === 'left', 'c21-align-right' : g.align !== 'left'}"
@@ -50,8 +50,18 @@ body {
50
50
  margin:0px;
51
51
  }
52
52
 
53
- :focus {outline: none !important;}
54
- ::-moz-focus-inner {border:0;}
53
+ /* Keyboard focus ring scoped to the widget host only (WCAG 2.4.7) */
54
+ chat-root *:focus:not(:focus-visible) {
55
+ outline: none;
56
+ }
57
+ chat-root *:focus-visible {
58
+ outline: 2px solid var(--c21-focus-ring, #1a73e8);
59
+ outline-offset: 2px;
60
+ }
61
+
62
+ ::-moz-focus-inner {
63
+ border: 0;
64
+ }
55
65
 
56
66
 
57
67
 
@@ -242,10 +252,7 @@ chat-root {
242
252
  height: 100%;
243
253
 
244
254
  // ========= BEGIN: RESET CSS TAG ========= //
245
- *:focus {
246
- outline: none;
247
- }
248
- button{
255
+ button {
249
256
  cursor: pointer;
250
257
  }
251
258
 
@@ -255,16 +262,21 @@ chat-root {
255
262
 
256
263
  textarea,
257
264
  textarea:visited,
258
- textarea:focus,
259
265
  textarea:hover,
260
- textarea:active{
261
- // all: unset;
266
+ textarea:active {
262
267
  line-height: 20px!important;
263
- outline: none!important;
264
- box-shadow:none!important;
268
+ box-shadow: none!important;
265
269
  border: 0px solid var(--trasp-light-black)!important;
266
270
  }
267
271
 
272
+ textarea:focus:not(:focus-visible) {
273
+ outline: none!important;
274
+ }
275
+
276
+ textarea:focus-visible {
277
+ outline: 0px !important;
278
+ }
279
+
268
280
  // ========= BEGIN: CSS APP.COMPONENT ========= //
269
281
  .c21-button-clean {
270
282
  -webkit-appearance: initial;
@@ -403,7 +415,7 @@ chat-root {
403
415
  }
404
416
  }
405
417
  button.c21-button-primary:hover,
406
- button.c21-button-primary:focus,
418
+ button.c21-button-primary:focus:not(:focus-visible),
407
419
  button.c21-button-primary:active {
408
420
  //outline-width: 0px;
409
421
  opacity: 0.8;
@@ -419,10 +431,9 @@ chat-root {
419
431
  cursor: pointer;
420
432
  }
421
433
  button.c21-button-link:hover,
422
- button.c21-button-link:focus,
434
+ button.c21-button-link:focus:not(:focus-visible),
423
435
  button.c21-button-link:active {
424
436
  text-decoration: underline;
425
- outline: none;
426
437
  }
427
438
 
428
439
  .c21-divBudge {
@@ -1,3 +1,4 @@
1
+ import { NO_ERRORS_SCHEMA } from '@angular/core';
1
2
  import { Triggerhandler } from './../chat21-core/utils/triggerHandler';
2
3
  import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3
4
  import { RouterTestingModule } from '@angular/router/testing';
@@ -20,25 +21,32 @@ import { PresenceService } from 'src/chat21-core/providers/abstract/presence.ser
20
21
  import { UploadService } from 'src/chat21-core/providers/abstract/upload.service';
21
22
  import { AppConfigService } from './providers/app-config.service';
22
23
  import { ChatManager } from 'src/chat21-core/providers/chat-manager';
23
- import { NGXLogger } from 'ngx-logger';
24
+ import { Globals } from './utils/globals';
25
+
26
+
24
27
  import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
25
28
  import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
26
29
  import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
30
+ import { of } from 'rxjs';
27
31
 
28
32
  describe('AppComponent', () => {
29
33
  let component: AppComponent;
30
34
  let fixture: ComponentFixture<AppComponent>;
31
- let ngxlogger: NGXLogger;
32
- let customLogger = new CustomLogger(ngxlogger)
35
+ const ngxlogger = jasmine.createSpyObj('NGXLogger', ['log', 'trace', 'debug', 'warn', 'error', 'info']);
36
+ const customLogger = new CustomLogger(ngxlogger);
33
37
 
34
38
  beforeEach(waitForAsync(() => {
39
+ LoggerInstance.setInstance(customLogger);
40
+ spyOn(AppComponent.prototype, 'ngAfterViewInit').and.stub();
35
41
  TestBed.configureTestingModule({
36
42
  declarations: [
37
43
  AppComponent
38
44
  ],
39
45
  imports: [RouterTestingModule,
40
46
  TranslateModule.forRoot()],
47
+ schemas: [NO_ERRORS_SCHEMA],
41
48
  providers: [
49
+ Globals,
42
50
  Triggerhandler,
43
51
  GlobalSettingsService,
44
52
  AppStorageService,
@@ -63,11 +71,18 @@ describe('AppComponent', () => {
63
71
  }));
64
72
 
65
73
  beforeEach(() => {
74
+ spyOn(TestBed.inject(GlobalSettingsService), 'getProjectParametersById').and.returnValue(
75
+ of({ project: null }) as any,
76
+ );
77
+ spyOn(TestBed.inject(GlobalSettingsService), 'manageLoadingDomains').and.returnValue(true);
78
+
79
+ const translator = TestBed.inject(TranslatorService);
80
+ spyOn(translator, 'initI18n').and.returnValue(Promise.resolve(undefined));
81
+ spyOn(translator, 'translate').and.stub();
82
+ spyOn(translator, 'getLanguage').and.returnValue('en');
83
+
66
84
  fixture = TestBed.createComponent(AppComponent);
67
85
  component = fixture.componentInstance;
68
- LoggerInstance.setInstance(customLogger)
69
- let logger = LoggerInstance.getInstance()
70
- component['logger']= logger
71
86
  fixture.detectChanges();
72
87
  });
73
88
 
@@ -106,17 +106,16 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
106
106
 
107
107
  forceDisconnect: boolean = false;
108
108
 
109
+ //network status
110
+ isOnline: boolean = true;
111
+ loading: boolean = false;
112
+ private calloutScheduleTimeout: any = null;
113
+
109
114
  // alert error message
110
115
  isShowErrorMessage: boolean = false;
111
116
  errorMessage: string = '';
112
117
  errorKeyMessage: string = null;
113
118
  errorParams: Record<string, any> = {};
114
-
115
- //network status
116
- isOnline: boolean = true;
117
-
118
- loading: boolean = false;
119
- private calloutScheduleTimeout: any = null;
120
119
 
121
120
  private logger: LoggerService = LoggerInstance.getInstance();
122
121
  constructor(
@@ -170,13 +169,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
170
169
  if (conversation.attributes && conversation.attributes['subtype'] === 'info') {
171
170
  return;
172
171
  }
173
- if (conversation.is_new && this.isInitialized) {
172
+ if (conversation.is_new && that.isInitialized) {
174
173
  that.manageTabNotification(false, 'conv-added')
175
174
  // this.soundMessage();
176
175
  }
177
- if(this.g.isOpen === false){
178
- that.lastConversation = conversation;
176
+ if(this.g.isOpen === false && conversation.sender !== this.g.senderId && !isInfo(conversation)){
179
177
  that.g.isOpenNewMessage = true;
178
+ that.lastConversation = conversation;
180
179
  }
181
180
  } else {
182
181
  //widget closed
@@ -224,6 +223,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
224
223
  that.lastConversation = conversation;
225
224
  that.g.isOpenNewMessage = true;
226
225
  that.logger.debug('[APP-COMP] lastconversationnn', that.lastConversation)
226
+ that.logger.debug('[APP-COMP] lastconversationnn message' + JSON.stringify(that.lastConversation?.attributes?.commands))
227
227
  }
228
228
  let badgeNewConverstionNumber = that.conversationsHandlerService.countIsNew()
229
229
  that.g.setParameter('conversationsBadge', badgeNewConverstionNumber);
@@ -2319,6 +2319,7 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
2319
2319
  this.el.nativeElement.style.setProperty('--chat-header-height', this.g.hideHeaderConversation? '0px': null)
2320
2320
  this.el.nativeElement.style.setProperty('--font-size-bubble-message', this.g.fontSize)
2321
2321
  this.el.nativeElement.style.setProperty('--font-family-bubble-message', this.g.fontFamily)
2322
+ this.el.nativeElement.style.setProperty('--chat-footer-close-button-height', this.g.closeChatInConversation? '30px': '0px')
2322
2323
 
2323
2324
  }
2324
2325
 
@@ -16,6 +16,7 @@ import { ConversationFooterComponent } from './component/conversation-detail/con
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
18
  import { ConversationAudioRecorderComponent } from './component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component';
19
+ import { StreamAudioSpectrumComponent } from './component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component';
19
20
  /** CONVERSATION-DETAIL COMPONENTS */
20
21
  import { BubbleMessageComponent } from './component/message/bubble-message/bubble-message.component';
21
22
  import { AvatarComponent } from './component/message/avatar/avatar.component';
@@ -25,6 +26,7 @@ import { InfoMessageComponent } from './component/message/info-message/info-mess
25
26
  import { HtmlComponent } from './component/message/html/html.component';
26
27
  import { FrameComponent } from './component/message/frame/frame.component';
27
28
  import { AudioComponent } from './component/message/audio/audio.component';
29
+ import { AudioSyncComponent } from './component/message/audio-sync/audio-sync.component';
28
30
  import { UserTypingComponent } from './../chat21-core/utils/user-typing/user-typing.component';
29
31
  /** MESSAGE ATTACHMENTS COMPONENTS */
30
32
  import { MessageAttachmentComponent } from './component/message-attachment/message-attachment.component';
@@ -56,6 +58,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
56
58
  import { APP_INITIALIZER, NgModule } from '@angular/core';
57
59
  import { HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
58
60
  import { FormsModule, ReactiveFormsModule } from '@angular/forms';
61
+ import { A11yModule } from '@angular/cdk/a11y';
59
62
  import { environment } from 'src/environments/environment';
60
63
 
61
64
  //THIRD-PART MODULES
@@ -136,8 +139,14 @@ import { Rules } from './utils/rules';
136
139
  import { ScriptService } from 'src/chat21-core/providers/scripts/script.service';
137
140
  import { CarouselComponent } from './component/message/carousel/carousel.component';
138
141
  import { BrandService } from './providers/brand.service';
142
+ import { OpenAiVoiceProviderService } from './providers/voice/STT&TTS/openai-voice.provider';
143
+ import {
144
+ SpeechToTextProvider,
145
+ TextToSpeechProvider,
146
+ } from './providers/voice/STT&TTS/speech-provider.abstract';
139
147
  import { ErrorAlertComponent } from './component/error-alert/error-alert.component';
140
148
  import { ConfirmCloseComponent } from './modals/confirm-close/confirm-close.component';
149
+ import { JsonSourcesComponent } from './component/message/json-sources/json-sources.component';
141
150
 
142
151
 
143
152
 
@@ -288,6 +297,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
288
297
  ConversationPreviewComponent,
289
298
  ConversationInternalFrameComponent,
290
299
  ConversationAudioRecorderComponent,
300
+ StreamAudioSpectrumComponent,
291
301
  BubbleMessageComponent,
292
302
  AvatarComponent,
293
303
  FrameComponent,
@@ -300,6 +310,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
300
310
  LinkButtonComponent,
301
311
  TextButtonComponent,
302
312
  AudioComponent,
313
+ AudioSyncComponent,
303
314
  UserTypingComponent,
304
315
  /**DIRECTIVES */
305
316
  HtmlEntitiesEncodePipe,
@@ -309,6 +320,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
309
320
  LikeUnlikeComponent,
310
321
  TooltipDirective,
311
322
  CarouselComponent,
323
+ JsonSourcesComponent,
312
324
  ErrorAlertComponent,
313
325
  ConfirmCloseComponent
314
326
  ],
@@ -316,6 +328,7 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
316
328
  BrowserAnimationsModule,
317
329
  FormsModule,
318
330
  ReactiveFormsModule,
331
+ A11yModule,
319
332
  PickerModule,
320
333
  TranslateModule.forRoot({
321
334
  // defaultLanguage: 'en',
@@ -405,6 +418,8 @@ export function uploadFactory(http: HttpClient, appConfig: AppConfigService, app
405
418
  WaitingService,
406
419
  ScriptService,
407
420
  BrandService,
421
+ { provide: SpeechToTextProvider, useExisting: OpenAiVoiceProviderService },
422
+ { provide: TextToSpeechProvider, useExisting: OpenAiVoiceProviderService },
408
423
  provideHttpClient(withInterceptorsFromDi())
409
424
  ],
410
425
  bootstrap: [AppComponent]
@@ -1,10 +1,14 @@
1
1
 
2
- <!-- tabindex="1500"-->
3
- <!-- onFocus="document.querySelector('[start-focus-chat21-conversation-component]').focus()" -->
4
- <div id="chat21-conversation-component"
2
+ <div id="chat21-conversation-component"
5
3
  #afConversationComponent
6
- tabindex="1500"
7
- aria-modal="true">
4
+ role="region"
5
+ [attr.aria-label]="g?.project?.widgetTitle">
6
+
7
+ <!-- Skip-link: revealed only on focus, jumps straight to the message composer (WCAG 2.4.1 Bypass Blocks). -->
8
+ <a class="c21-skip-link" href="#chat21-main-message-context"
9
+ (click)="$event.preventDefault(); skipToCompose()">
10
+ {{ translationMapFooter?.get('SKIP_TO_COMPOSER') || 'Skip to message composer' }}
11
+ </a>
8
12
 
9
13
  <!-- HEADER -->
10
14
  <chat-conversation-header
@@ -66,6 +70,7 @@
66
70
  [showThinkingMessage]="showThinkingMessage"
67
71
  [lastServerSenderKind]="lastServerSenderKind"
68
72
  [fullscreenMode]="g?.fullscreenMode"
73
+ [isStreamAudioActive]="isStreamAudioActive"
69
74
  [translationMap]="translationMapContent"
70
75
  [stylesMap]="stylesMap"
71
76
  (onBeforeMessageRender)="onBeforeMessageRenderFN($event)"
@@ -113,12 +118,16 @@
113
118
  </dialog>
114
119
 
115
120
  <!-- FOOTER -->
116
- <!-- [class.maximize-width]="(g?.singleConversation && hideTextAreaContent) || (isConversationArchived && !g?.allowReopen)" -->
117
121
  <div id="chat21-footer">
118
- <div *ngIf="showBadgeScroollToBottom" id="chat21-buttonToBottom" (click)="scrollToBottom()">
119
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path opacity=".87" fill="none" d="M24 24H0V0h24v24z"/><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z"/></svg>
120
- <div *ngIf="messagesBadgeCount!=0" id="chat21-divBudge" class="c21-divBudge">{{messagesBadgeCount}}</div>
121
- </div>
122
+ <button *ngIf="showBadgeScroollToBottom"
123
+ type="button"
124
+ id="chat21-buttonToBottom"
125
+ class="c21-button-clean"
126
+ [attr.aria-label]="translationMapContent?.get('BUTTON_SCROLL_TO_BOTTOM') || 'Scroll to last message'"
127
+ (click)="scrollToBottom()">
128
+ <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path opacity=".87" fill="none" d="M24 24H0V0h24v24z"/><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z"/></svg>
129
+ <span *ngIf="messagesBadgeCount!=0" id="chat21-divBudge" class="c21-divBudge" aria-hidden="true">{{messagesBadgeCount}}</span>
130
+ </button>
122
131
 
123
132
  <chat-conversation-footer
124
133
  [conversationWith]="conversationWith"
@@ -132,12 +141,14 @@
132
141
  [showAttachmentFooterButton]="g?.showAttachmentFooterButton"
133
142
  [showEmojiFooterButton]="g?.showEmojiFooterButton"
134
143
  [showAudioRecorderFooterButton]="g?.showAudioRecorderFooterButton"
144
+ [showAudioStreamFooterButton]="g?.showAudioStreamFooterButton"
135
145
  [hideTextAreaContent]="(g?.singleConversation && hideTextAreaContent) || (isConversationArchived && !g?.allowReopen)"
136
146
  [isConversationArchived]="isConversationArchived"
137
147
  [hideTextReply]="hideFooterTextReply"
138
148
  [isMobile]="g?.isMobile"
139
149
  [isEmojiiPickerShow]="isEmojiiPickerShow"
140
150
  [footerMessagePlaceholder]="footerMessagePlaceholder"
151
+ [closeChatInConversation]="g?.closeChatInConversation"
141
152
  [fileUploadAccept]="g?.fileUploadAccept"
142
153
  [dropEvent]="dropEvent"
143
154
  [poweredBy]="g?.poweredBy"
@@ -148,7 +159,10 @@
148
159
  (onAfterSendMessage)="onAfterSendMessageFN($event)"
149
160
  (onChangeTextArea)="onChangeTextArea($event)"
150
161
  (onAttachmentFileButtonClicked)="onAttachmentFileButtonClicked($event)"
151
- (onNewConversationButtonClicked)="onNewConversationButtonClickedFN($event)">
162
+ (onNewConversationButtonClicked)="onNewConversationButtonClickedFN($event)"
163
+ (onStreamAudioActiveChange)="onStreamAudioActiveChange($event)"
164
+ (onStreamAudioConnectingChange)="onStreamAudioConnectingChange($event)"
165
+ (onCloseChatButtonClicked)="onCloseChatButtonClickedFN($event)">
152
166
  </chat-conversation-footer>
153
167
 
154
168
  </div>
@@ -13,6 +13,34 @@
13
13
  bottom: var(--chat-footer-height) !important
14
14
  }
15
15
 
16
+ /* Skip-link visible only when focused via keyboard (WCAG 2.4.1) */
17
+ .c21-skip-link {
18
+ position: absolute;
19
+ top: -40px;
20
+ left: 8px;
21
+ z-index: 100;
22
+ padding: 6px 12px;
23
+ background-color: #1a73e8;
24
+ color: #ffffff !important;
25
+ border-radius: 4px;
26
+ font-size: 14px;
27
+ text-decoration: none;
28
+ transition: top 0.15s ease-in-out;
29
+ }
30
+
31
+ .c21-skip-link:focus,
32
+ .c21-skip-link:focus-visible {
33
+ top: 8px;
34
+ outline: 2px solid #ffffff;
35
+ outline-offset: 2px;
36
+ }
37
+
38
+ @media (prefers-reduced-motion: reduce) {
39
+ .c21-skip-link {
40
+ transition: none;
41
+ }
42
+ }
43
+
16
44
  // ============= CSS #chat21-conversation-component ================= //
17
45
  #chat21-conversation-component {
18
46
  // background-color: #ffffff;
@@ -137,7 +165,7 @@
137
165
  #dropZone_container{
138
166
  position: absolute;
139
167
  top: 52px;
140
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
168
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
141
169
  left: 0;
142
170
  right: 0;
143
171
  background-color: rgba(240,248,255,0.6);
@@ -240,7 +268,17 @@ dialog:-internal-dialog-in-top-layer{
240
268
 
241
269
 
242
270
  ::ng-deep .chat21-sheet-content{
243
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + 34px)!important;
271
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height) + 34px)!important;
272
+ }
273
+
274
+ /* Con `.close-stream-button` (stream in ascolto): spazio per alert stream sopra il footer */
275
+ #chat21-conversation-component.chat21-conversation--close-stream-active ::ng-deep .chat21-sheet-content {
276
+ bottom: calc(
277
+ var(--chat-footer-logo-height) +
278
+ var(--chat-footer-height) +
279
+ var(--chat-footer-stream-button-height) +
280
+ 34px
281
+ ) !important;
244
282
  }
245
283
 
246
284
  }