@chat21/chat21-web-widget 5.1.33-rc9 → 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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/app/component/conversation-detail/conversation/conversation.component.ts +3 -1
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +0 -7
- package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +7 -5
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +4 -3
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +18 -18
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +6 -0
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +8 -5
- package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +5 -1
- package/src/app/component/form/inputs/form-text/form-text.component.ts +9 -3
- package/src/app/component/message/bubble-message/bubble-message.component.scss +5 -0
- package/src/app/component/message/bubble-message/bubble-message.component.ts +14 -0
- package/src/app/component/message/json-sources/json-sources.component.scss +12 -8
- package/src/app/pipe/marked.pipe.ts +51 -41
- package/src/app/providers/global-settings.service.ts +31 -0
- package/src/app/providers/json-sources-parser.service.ts +25 -32
- package/src/app/providers/voice/voice-streaming.service.ts +11 -19
- package/src/app/providers/voice/voice-streaming.types.ts +0 -1
- package/src/app/providers/voice/voice.service.spec.ts +12 -45
- package/src/app/providers/voice/voice.service.ts +215 -45
- package/src/app/utils/globals.ts +10 -0
- package/src/assets/i18n/en.json +106 -125
- package/src/assets/i18n/es.json +1 -0
- package/src/assets/i18n/fr.json +1 -0
- package/src/assets/i18n/it.json +1 -0
- package/src/assets/sounds/keyboard.mp3 +0 -0
- package/src/assets/twp/chatbot-panel.html +3 -1
- package/src/chat21-core/utils/utils-message.ts +15 -5
- package/src/widget-config-template.json +1 -0
- package/src/widget-config.json +30 -28
- package/.playwright-mcp/console-2026-05-08T15-31-09-000Z.log +0 -17
- package/.playwright-mcp/console-2026-05-08T15-32-19-412Z.log +0 -89
- package/.playwright-mcp/console-2026-05-08T16-18-48-424Z.log +0 -133
- package/.playwright-mcp/console-2026-05-11T12-54-06-869Z.log +0 -13
- package/.playwright-mcp/console-2026-05-11T12-54-56-229Z.log +0 -147
- package/.playwright-mcp/console-2026-05-11T12-55-47-174Z.log +0 -183
- package/.playwright-mcp/console-2026-05-11T15-34-03-590Z.log +0 -210
- package/.playwright-mcp/console-2026-05-12T15-07-31-880Z.log +0 -118
- package/.playwright-mcp/page-2026-05-08T15-32-19-900Z.yml +0 -851
- package/.playwright-mcp/page-2026-05-08T15-32-47-264Z.yml +0 -857
- package/.playwright-mcp/page-2026-05-08T15-33-17-089Z.yml +0 -1110
- package/.playwright-mcp/page-2026-05-08T15-33-23-486Z.yml +0 -1069
- package/.playwright-mcp/page-2026-05-08T15-33-45-390Z.yml +0 -1076
- package/.playwright-mcp/page-2026-05-08T15-33-52-666Z.yml +0 -1072
- package/.playwright-mcp/page-2026-05-08T15-34-01-338Z.yml +0 -1085
- package/.playwright-mcp/page-2026-05-08T15-34-07-227Z.yml +0 -1072
- package/.playwright-mcp/page-2026-05-08T15-34-13-875Z.yml +0 -1072
- package/.playwright-mcp/page-2026-05-08T15-34-21-885Z.yml +0 -1109
- package/.playwright-mcp/page-2026-05-08T15-34-32-755Z.yml +0 -1109
- package/.playwright-mcp/page-2026-05-08T15-35-09-607Z.yml +0 -1119
- package/.playwright-mcp/page-2026-05-08T15-35-14-242Z.yml +0 -1109
- package/.playwright-mcp/page-2026-05-08T16-18-48-671Z.yml +0 -44
- package/.playwright-mcp/page-2026-05-08T16-18-52-753Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-19-13-919Z.yml +0 -68
- package/.playwright-mcp/page-2026-05-08T16-19-17-977Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-19-25-733Z.yml +0 -120
- package/.playwright-mcp/page-2026-05-08T16-19-29-252Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-19-39-269Z.yml +0 -80
- package/.playwright-mcp/page-2026-05-08T16-19-43-915Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-20-04-407Z.yml +0 -81
- package/.playwright-mcp/page-2026-05-08T16-20-08-984Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-20-32-397Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-20-58-658Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-21-12-320Z.yml +0 -86
- package/.playwright-mcp/page-2026-05-08T16-21-39-154Z.yml +0 -91
- package/.playwright-mcp/page-2026-05-08T16-21-45-420Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-22-21-062Z.yml +0 -0
- package/.playwright-mcp/page-2026-05-08T16-22-58-232Z.yml +0 -91
- package/.playwright-mcp/page-2026-05-08T16-23-36-520Z.yml +0 -0
- package/.playwright-mcp/page-2026-05-08T16-23-46-805Z.yml +0 -100
- package/.playwright-mcp/page-2026-05-08T16-23-55-169Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-24-26-574Z.yml +0 -91
- package/.playwright-mcp/page-2026-05-08T16-25-34-414Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-25-59-831Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-26-21-809Z.yml +0 -91
- package/.playwright-mcp/page-2026-05-08T16-26-47-443Z.yml +0 -105
- package/.playwright-mcp/page-2026-05-08T16-26-56-136Z.png +0 -0
- package/.playwright-mcp/page-2026-05-08T16-27-59-610Z.yml +0 -48
- package/.playwright-mcp/page-2026-05-11T12-54-07-180Z.yml +0 -44
- package/.playwright-mcp/page-2026-05-11T12-54-56-946Z.yml +0 -4
- package/.playwright-mcp/page-2026-05-11T12-55-47-503Z.yml +0 -24
- package/.playwright-mcp/page-2026-05-11T12-56-00-766Z.yml +0 -28
- package/.playwright-mcp/page-2026-05-11T12-56-06-438Z.yml +0 -90
- package/.playwright-mcp/page-2026-05-11T12-57-56-838Z.yml +0 -106
- package/.playwright-mcp/page-2026-05-11T12-58-00-124Z.yml +0 -106
- package/.playwright-mcp/page-2026-05-11T12-59-08-836Z.yml +0 -61
- package/.playwright-mcp/page-2026-05-11T12-59-12-088Z.yml +0 -61
- package/.playwright-mcp/page-2026-05-11T12-59-26-215Z.yml +0 -69
- package/.playwright-mcp/page-2026-05-11T12-59-29-519Z.yml +0 -69
- package/.playwright-mcp/page-2026-05-11T12-59-37-309Z.yml +0 -0
- package/.playwright-mcp/page-2026-05-11T12-59-39-968Z.yml +0 -79
- package/.playwright-mcp/page-2026-05-11T12-59-45-983Z.yml +0 -78
- package/.playwright-mcp/page-2026-05-11T12-59-49-951Z.yml +0 -78
- package/.playwright-mcp/page-2026-05-11T15-34-04-515Z.yml +0 -0
- package/.playwright-mcp/page-2026-05-12T15-07-32-171Z.yml +0 -44
- package/.playwright-mcp/page-2026-05-12T15-08-09-820Z.yml +0 -119
- package/docs/TILEDESK_WIDGET_ACCESSIBILITY_STATEMENT_COMPLETE.md +0 -379
- package/playwright-report/index.html +0 -90
- package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component copy.html +0 -172
- package/test-results/.last-run.json +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,20 @@
|
|
|
6
6
|
### **Copyrigth**:
|
|
7
7
|
*Tiledesk SRL*
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
# 5.1.34-rc1
|
|
11
|
+
- **added**: onPageChangeVisibilityDesktop:'open' and 'onPageChangeVisibilityMobile: 'open' in chatbot-panel.html file
|
|
12
|
+
|
|
13
|
+
# 5.1.33-rc12
|
|
14
|
+
- **bug-fixed**: if last message is ulr_preview shows previous message buttons
|
|
15
|
+
|
|
16
|
+
# 5.1.33-rc11
|
|
17
|
+
- **changed**: **Stream audio** — updated the streaming/voice-mode footer icon.
|
|
18
|
+
- **added**: **Stream audio** — tooltip on the stream button (i18n key `STREAM_AUDIO`, e.g. “Use voice mode”) via SVG `<title>` and `aria-label`.
|
|
19
|
+
|
|
20
|
+
# 5.1.33-rc10
|
|
21
|
+
- **bug-fixed**: fixed bug with knowledge base json sources without URLs
|
|
22
|
+
|
|
9
23
|
# 5.1.33-rc9
|
|
10
24
|
- **changed**: **Conversation footer** — accessibility-focused markup (ARIA roles/labels, live regions, semantic send control), stream-audio layout (wrapper + voice mode: hide attach/emoji while streaming, inline status, stream button + spectrum), optional **Close chat** action when `closeChatInConversation` is enabled; emoji restriction alert uses assertive live region semantics.
|
|
11
25
|
- **bug-fixed**: **`getConversationDetail` Tiledesk fallback** — when `getMyRequests()` rejects, the conversation is treated as archived (`isConversationArchived = true`) and the handler returns immediately instead of resetting state from an empty fallback payload.
|
package/package.json
CHANGED
|
@@ -264,7 +264,9 @@ export class ConversationComponent implements OnInit, AfterViewInit, OnChanges {
|
|
|
264
264
|
'CLOSE',
|
|
265
265
|
'VOICE_CONNECTING',
|
|
266
266
|
'VOICE_LISTENING',
|
|
267
|
-
'VOICE_PROCESSING'
|
|
267
|
+
'VOICE_PROCESSING',
|
|
268
|
+
'STREAM_AUDIO',
|
|
269
|
+
'MAX_ATTACHMENT'
|
|
268
270
|
];
|
|
269
271
|
|
|
270
272
|
const keysContent = [
|
|
@@ -264,11 +264,4 @@ describe('ConversationContentComponent', () => {
|
|
|
264
264
|
});
|
|
265
265
|
});
|
|
266
266
|
|
|
267
|
-
describe('ngAfterContentChecked', () => {
|
|
268
|
-
it('should trigger change detection', () => {
|
|
269
|
-
spyOn((component as any).cdref, 'detectChanges');
|
|
270
|
-
component.ngAfterContentChecked();
|
|
271
|
-
expect((component as any).cdref.detectChanges).toHaveBeenCalled();
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
267
|
});
|
package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
|
1
|
+
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, OnDestroy, Output, SimpleChanges, ViewChild } from '@angular/core';
|
|
2
|
+
import { Subscription } from 'rxjs';
|
|
2
3
|
import { MAX_WIDTH_IMAGES, MSG_STATUS_RETURN_RECEIPT, MSG_STATUS_SENT, MSG_STATUS_SENT_SERVER } from 'src/app/utils/constants';
|
|
3
4
|
import { MessageModel } from 'src/chat21-core/models/message';
|
|
4
5
|
import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
|
|
@@ -12,7 +13,7 @@ import { isCarousel, isEmojii, isFirstMessage, isFrame, isImage, isInfo, isLastM
|
|
|
12
13
|
templateUrl: './conversation-content.component.html',
|
|
13
14
|
styleUrls: ['./conversation-content.component.scss']
|
|
14
15
|
})
|
|
15
|
-
export class ConversationContentComponent implements OnInit {
|
|
16
|
+
export class ConversationContentComponent implements OnInit, OnDestroy {
|
|
16
17
|
@ViewChild('scrollMe') public scrollMe: ElementRef;
|
|
17
18
|
|
|
18
19
|
@Input() messages: MessageModel[]
|
|
@@ -73,6 +74,7 @@ export class ConversationContentComponent implements OnInit {
|
|
|
73
74
|
showUploadProgress: boolean = false;
|
|
74
75
|
fileType: string;
|
|
75
76
|
private logger: LoggerService = LoggerInstance.getInstance();
|
|
77
|
+
private uploadSub?: Subscription;
|
|
76
78
|
|
|
77
79
|
constructor(private cdref: ChangeDetectorRef,
|
|
78
80
|
private elementRef: ElementRef,
|
|
@@ -82,8 +84,8 @@ export class ConversationContentComponent implements OnInit {
|
|
|
82
84
|
this.listenToUploadFileProgress();
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
+
ngOnDestroy() {
|
|
88
|
+
this.uploadSub?.unsubscribe();
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
ngOnChanges(changes: SimpleChanges){
|
|
@@ -121,7 +123,7 @@ export class ConversationContentComponent implements OnInit {
|
|
|
121
123
|
|
|
122
124
|
// ENABLE HTML SECTION 'FILE PENDING UPLOAD'
|
|
123
125
|
listenToUploadFileProgress() {
|
|
124
|
-
this.uploadService.BSStateUpload.subscribe((data: any) => {
|
|
126
|
+
this.uploadSub = this.uploadService.BSStateUpload.subscribe((data: any) => {
|
|
125
127
|
this.logger.debug('[CONV-CONTENT] BSStateUpload', data);
|
|
126
128
|
// && data.type.startsWith("application")
|
|
127
129
|
if (data) {
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html
CHANGED
|
@@ -59,7 +59,9 @@
|
|
|
59
59
|
<span class="v-align-center">
|
|
60
60
|
<svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" width="24px" height="24" viewBox="0 0 24 24" fill="currentColor">
|
|
61
61
|
<path d="M9.9,22.7c0,0-.1,0-.2,0-1.9.3-3.7-.2-5.2-1.4-3-2.3-3.6-6.4-1.4-9.5L9.5,2.5c.4-.5,1.1-.6,1.6-.3.5.4.6,1.1.3,1.6l-6.5,9.4c-1.4,2-1,4.8.9,6.3,1,.8,2.2,1.1,3.5.9,1.3-.2,2.4-.9,3.1-1.9l6-8.7c.9-1.2.6-3-.6-3.9-.6-.5-1.4-.6-2.1-.5-.8.1-1.4.5-1.9,1.1l-5.8,8.2c-.3.5-.2,1.1.2,1.5.2.2.5.3.8.2.3,0,.6-.2.7-.4l4.7-6.2c.4-.5,1.1-.6,1.6-.2.5.4.6,1.1.2,1.6l-4.7,6.2c-.5.7-1.4,1.2-2.3,1.3-.9.1-1.8-.2-2.5-.7-1.4-1.1-1.6-3.1-.6-4.6l5.8-8.2c.8-1.1,2-1.9,3.4-2.1,1.4-.2,2.7.1,3.8,1,2.2,1.7,2.7,4.8,1.1,7.1l-6,8.7c-1.1,1.5-2.6,2.5-4.4,2.8h0Z"/>
|
|
62
|
-
|
|
62
|
+
<title id="altIconTitle">{{ maxAttachmentLabel }}</title>
|
|
63
|
+
</svg>
|
|
64
|
+
|
|
63
65
|
</span>
|
|
64
66
|
<input
|
|
65
67
|
[attr.disabled]="(isFilePendingToUpload || isConversationArchived || hideTextReply)? true : null"
|
|
@@ -173,8 +175,7 @@
|
|
|
173
175
|
class="chat21-textarea-button chat21-stream-button"
|
|
174
176
|
[class.active]="isStreamAudioActive || isStreamAudioConnecting || (!textInputTextArea && !hideTextReply)"
|
|
175
177
|
[class.chat21-stream-button--expanded]="isStreamAudioActive || isStreamAudioConnecting"
|
|
176
|
-
(click)="onStreamPressed($event)" [attr.aria-label]="(isStreamAudioActive || isStreamAudioConnecting) ? (translationMap?.get('CLOSE') || 'Chiudi stream') : (translationMap?.get('STREAM_AUDIO') || 'Stream audio')"
|
|
177
|
-
[ngStyle]="{ 'background-color': stylesMap?.get('iconColor') || stylesMap?.get('themeColor') }">
|
|
178
|
+
(click)="onStreamPressed($event)" [attr.aria-label]="(isStreamAudioActive || isStreamAudioConnecting) ? (translationMap?.get('CLOSE') || 'Chiudi stream') : (translationMap?.get('STREAM_AUDIO') || 'Stream audio')">
|
|
178
179
|
<chat-stream-audio-spectrum
|
|
179
180
|
mode="button"
|
|
180
181
|
[active]="isStreamAudioActive || isStreamAudioConnecting"
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
align-items: center;
|
|
9
9
|
justify-content: space-between;
|
|
10
10
|
gap: 8px;
|
|
11
|
+
|
|
12
|
+
//if attachment icon OR emoji icon is not in DOM -> increment textarea width
|
|
13
|
+
&:has(:not(#chat21-start-upload-doc), :not(#chat21-emoticon-picker)) .visible-text-area {
|
|
14
|
+
width: 80%;
|
|
15
|
+
}
|
|
16
|
+
//if attachment icon AND emoji icon is not in DOM -> increment textarea width
|
|
17
|
+
&:has(:not(#chat21-start-upload-doc)):has(:not(#chat21-emoticon-picker)) .visible-text-area {
|
|
18
|
+
width: 90%;
|
|
19
|
+
}
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
.close-chat-container{
|
|
@@ -105,28 +114,19 @@
|
|
|
105
114
|
box-sizing: border-box;
|
|
106
115
|
flex-shrink: 0;
|
|
107
116
|
color: #ffffff;
|
|
108
|
-
transition:
|
|
109
|
-
min-width 220ms ease,
|
|
110
|
-
border-radius 180ms ease,
|
|
111
|
-
padding 180ms ease;
|
|
112
|
-
|
|
113
|
-
.chat21-stream-button__icon svg {
|
|
114
|
-
width: 20px;
|
|
115
|
-
height: 20px;
|
|
116
|
-
path {
|
|
117
|
-
fill: #ffffff;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
117
|
+
transition: min-width 220ms ease, border-radius 180ms ease, padding 180ms ease;
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
background: rgba(
|
|
123
|
-
|
|
119
|
+
&:hover{
|
|
120
|
+
background: rgba(240, 240, 240, 0.4) !important;
|
|
121
|
+
transition: all 0.45s ease-in-out 0s !important;
|
|
122
|
+
-moz-transition: all 0.45s ease-in-out 0s !important;
|
|
123
|
+
-webkit-transition: all 0.45s ease-in-out 0s !important;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
&.chat21-stream-button--expanded {
|
|
127
|
-
min-width:
|
|
127
|
+
min-width: 110px;
|
|
128
128
|
border-radius: 999px;
|
|
129
|
-
padding: 0 14px;
|
|
129
|
+
// padding: 0 14px;
|
|
130
130
|
justify-content: center;
|
|
131
131
|
|
|
132
132
|
// stop the circle-hover background from looking odd on pill
|
|
@@ -427,7 +427,7 @@ textarea:active{
|
|
|
427
427
|
flex-direction: column;
|
|
428
428
|
justify-content: center;
|
|
429
429
|
position: absolute;
|
|
430
|
-
padding: 8px 16px;
|
|
430
|
+
// padding: 8px 16px;
|
|
431
431
|
overflow: hidden;
|
|
432
432
|
background-color: color-mix(in srgb, var(--content-background-color) 34%, transparent);
|
|
433
433
|
backdrop-filter: blur(20px) saturate(1.2);
|
package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
CHANGED
|
@@ -124,6 +124,12 @@ export class ConversationFooterComponent implements OnInit, OnChanges, OnDestroy
|
|
|
124
124
|
return this.translationMap?.get('VOICE_LISTENING') || 'Listening...';
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
get maxAttachmentLabel(): string {
|
|
128
|
+
const template = this.translationMap?.get('MAX_ATTACHMENT')
|
|
129
|
+
|| `Max allowed size {{FILE_SIZE_LIMIT}}Mb`;
|
|
130
|
+
return template.replace(/\{\{FILE_SIZE_LIMIT\}\}/g, String(this.file_size_limit));
|
|
131
|
+
}
|
|
132
|
+
|
|
127
133
|
file_size_limit = FILE_SIZE_LIMIT;
|
|
128
134
|
attachmentTooltip: string = '';
|
|
129
135
|
isErrorNetwork: boolean = false;
|
|
@@ -22,11 +22,14 @@
|
|
|
22
22
|
<!-- BUTTON: inactive icon / expanded pill content -->
|
|
23
23
|
<ng-container *ngSwitchCase="'button'">
|
|
24
24
|
<span class="stream-audio-button__icon" *ngIf="!active" aria-hidden="true">
|
|
25
|
-
<svg role="img" xmlns="http://www.w3.org/2000/svg"
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
<
|
|
25
|
+
<svg role="img" xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 23 23" aria-labelledby="altIconTitle">
|
|
26
|
+
<rect x="0" y="7.5" height="6px" fill-opacity="1" width="2px" rx="0.75" ry="0.75"></rect>
|
|
27
|
+
<rect x="4" y="5.5" height="10px" fill-opacity="1" width="2px" rx="0.75" ry="0.75"></rect>
|
|
28
|
+
<rect x="8" y="2.5" height="16px" fill-opacity="1" width="2px" rx="0.75" ry="0.75"></rect>
|
|
29
|
+
<rect x="12" y="5.5" height="10px" fill-opacity="1" width="2px" rx="0.75" ry="0.75"></rect>
|
|
30
|
+
<rect x="16" y="2.5" height="16px" fill-opacity="1" width="2px" rx="0.75" ry="0.75"></rect>
|
|
31
|
+
<rect x="20" y="7.5" height="6px" fill-opacity="1" width="2px" rx="0.75" ry="0.75"></rect>
|
|
32
|
+
<title id="altIconTitle">{{ translationMap.get('STREAM_AUDIO') }}</title>
|
|
30
33
|
</svg>
|
|
31
34
|
</span>
|
|
32
35
|
|
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
width: 20px;
|
|
40
40
|
height: 20px;
|
|
41
41
|
display: block;
|
|
42
|
+
rect {
|
|
43
|
+
fill: var(--icon-fill-color);
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
.stream-audio-button__expanded {
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
gap: 12px;
|
|
49
52
|
width: 100%;
|
|
50
53
|
user-select: none;
|
|
54
|
+
color: var(--icon-fill-color);
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
.stream-audio-button__label {
|
|
@@ -74,6 +78,6 @@
|
|
|
74
78
|
width: 3px;
|
|
75
79
|
height: 100%;
|
|
76
80
|
border-radius: 2px;
|
|
77
|
-
background:
|
|
81
|
+
background: var(--icon-fill-color);
|
|
78
82
|
transform-origin: center;
|
|
79
83
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChange, ViewChild } from '@angular/core';
|
|
1
|
+
import { Component, ElementRef, EventEmitter, Input, OnInit, OnDestroy, Output, SimpleChange, ViewChild } from '@angular/core';
|
|
2
2
|
import { FormGroup, FormGroupDirective } from '@angular/forms';
|
|
3
|
+
import { Subscription } from 'rxjs';
|
|
3
4
|
import { FormArray } from '../../../../../chat21-core/models/formArray';
|
|
4
5
|
|
|
5
6
|
@Component({
|
|
@@ -7,7 +8,7 @@ import { FormArray } from '../../../../../chat21-core/models/formArray';
|
|
|
7
8
|
templateUrl: './form-text.component.html',
|
|
8
9
|
styleUrls: ['./form-text.component.scss']
|
|
9
10
|
})
|
|
10
|
-
export class FormTextComponent implements OnInit {
|
|
11
|
+
export class FormTextComponent implements OnInit, OnDestroy {
|
|
11
12
|
|
|
12
13
|
@Input() element: FormArray;
|
|
13
14
|
@Input() controlName: string;
|
|
@@ -19,6 +20,7 @@ export class FormTextComponent implements OnInit {
|
|
|
19
20
|
@ViewChild('div_input') input: ElementRef;
|
|
20
21
|
form: FormGroup<any>;
|
|
21
22
|
inputType: string = 'text'
|
|
23
|
+
private valueChangesSub?: Subscription;
|
|
22
24
|
|
|
23
25
|
get fieldBaseId(): string {
|
|
24
26
|
const raw = this.element?.name || this.controlName || 'field';
|
|
@@ -51,13 +53,17 @@ export class FormTextComponent implements OnInit {
|
|
|
51
53
|
ngOnInit() {
|
|
52
54
|
this.form = this.rootFormGroup.control as FormGroup<any>;
|
|
53
55
|
if(this.form && this.form.controls && this.form.controls[this.controlName]){
|
|
54
|
-
this.form.controls[this.controlName].valueChanges.subscribe((value) => {
|
|
56
|
+
this.valueChangesSub = this.form.controls[this.controlName].valueChanges.subscribe((value) => {
|
|
55
57
|
this.hasSubmitted= false;
|
|
56
58
|
this.setFormStyle();
|
|
57
59
|
})
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
ngOnDestroy() {
|
|
64
|
+
this.valueChangesSub?.unsubscribe();
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
ngOnChanges(changes: SimpleChange){
|
|
62
68
|
if(this.controlName && (this.controlName.toLowerCase().includes('email') || this.controlName.toLowerCase().includes('e-mail')) ){
|
|
63
69
|
this.inputType = 'email';
|
|
@@ -40,6 +40,18 @@ export class BubbleMessageComponent implements OnInit, OnDestroy {
|
|
|
40
40
|
|
|
41
41
|
@HostBinding('class.no-background') get hostNoBackground() { return this.jsonSources !== null && this.jsonSources.length > 0; }
|
|
42
42
|
@HostBinding('class.json-resources') get hostIsJsonResources() { return this.jsonSources !== null && this.jsonSources.length > 0; }
|
|
43
|
+
@HostBinding('class.hidden-bubble') get hostHiddenBubble() { return !this.hasRenderableContent(); }
|
|
44
|
+
|
|
45
|
+
hasRenderableContent(): boolean {
|
|
46
|
+
const msg = this.message;
|
|
47
|
+
if (!msg) return false;
|
|
48
|
+
if (isImage(msg) || isFile(msg) || isFrame(msg) || isAudio(msg)) return true;
|
|
49
|
+
if (this.jsonSources && this.jsonSources.length > 0) return true;
|
|
50
|
+
// For url_preview messages, `text` may carry the raw JSON payload (not display text):
|
|
51
|
+
// if sources parsing yielded nothing, the bubble must stay hidden.
|
|
52
|
+
if (this.isUrlPreviewMessage) return false;
|
|
53
|
+
return !!(msg.text && String(msg.text).trim().length > 0);
|
|
54
|
+
}
|
|
43
55
|
|
|
44
56
|
readonly isImage = isImage;
|
|
45
57
|
readonly isFile = isFile;
|
|
@@ -55,6 +67,7 @@ export class BubbleMessageComponent implements OnInit, OnDestroy {
|
|
|
55
67
|
sizeImage: { width: number; height: number } = { width: 0, height: 0 };
|
|
56
68
|
fullnameColor: string = '';
|
|
57
69
|
jsonSources: JsonSourceItem[] | null = null;
|
|
70
|
+
isUrlPreviewMessage = false;
|
|
58
71
|
|
|
59
72
|
private urlPreviewReqId = 0;
|
|
60
73
|
|
|
@@ -139,6 +152,7 @@ export class BubbleMessageComponent implements OnInit, OnDestroy {
|
|
|
139
152
|
this.message?.type === TYPE_MSG_URL_PREVIEW
|
|
140
153
|
|| this.message?.metadata?.type === TYPE_MSG_URL_PREVIEW
|
|
141
154
|
|| this.message?.attributes?.type === TYPE_MSG_URL_PREVIEW;
|
|
155
|
+
this.isUrlPreviewMessage = !!urlPreviewLike;
|
|
142
156
|
if (urlPreviewLike) this.loadJsonSourcesFromUrlPreviewMessage();
|
|
143
157
|
}
|
|
144
158
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
:host {
|
|
2
|
-
--panel-bck: #f7f8fa;
|
|
2
|
+
--panel-bck: #ffffff; // #f7f8fa;
|
|
3
3
|
--row-sep: rgba(66, 133, 244, 0.18);
|
|
4
4
|
--text: rgba(0, 0, 0, 0.90);
|
|
5
5
|
--muted: rgba(0, 0, 0, 0.62);
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.sources-header {
|
|
25
|
-
display:
|
|
25
|
+
display: none;
|
|
26
26
|
align-items: center;
|
|
27
27
|
gap: 8px;
|
|
28
28
|
padding: 2px 4px 8px 4px;
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
.sources-panel {
|
|
57
57
|
display: flex;
|
|
58
58
|
flex-direction: column;
|
|
59
|
-
padding:
|
|
59
|
+
padding: 18px 12px 10px 12px;
|
|
60
60
|
background: var(--panel-bck);
|
|
61
61
|
border-radius: 18px;
|
|
62
62
|
font-size: 14px;
|
|
@@ -79,6 +79,10 @@
|
|
|
79
79
|
width: 100%;
|
|
80
80
|
box-sizing: border-box;
|
|
81
81
|
|
|
82
|
+
border-bottom: 1px solid var(--row-sep);
|
|
83
|
+
border-bottom-left-radius: 0;
|
|
84
|
+
border-bottom-right-radius: 0;
|
|
85
|
+
|
|
82
86
|
&:hover {
|
|
83
87
|
background: rgba(255, 255, 255, 0.55);
|
|
84
88
|
.source-row__title {
|
|
@@ -86,11 +90,11 @@
|
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
& + & {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
93
|
+
// & + & {
|
|
94
|
+
// border-top: 1px solid var(--row-sep);
|
|
95
|
+
// border-top-left-radius: 0;
|
|
96
|
+
// border-top-right-radius: 0;
|
|
97
|
+
// }
|
|
94
98
|
|
|
95
99
|
.source-row__left {
|
|
96
100
|
min-width: 0;
|
|
@@ -5,61 +5,71 @@ import { marked, Tokens } from 'marked';
|
|
|
5
5
|
name: 'marked'
|
|
6
6
|
})
|
|
7
7
|
export class MarkedPipe implements PipeTransform {
|
|
8
|
+
private static renderer: any = null;
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
private static getRenderer(): any {
|
|
11
|
+
if (!MarkedPipe.renderer) {
|
|
12
|
+
const renderer = new marked.Renderer();
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
/* --------------------------------------------------
|
|
15
|
+
🔐 1. NON renderizzare HTML raw
|
|
16
|
+
-------------------------------------------------- */
|
|
17
|
+
renderer.html = function(token: Tokens.HTML | Tokens.Tag): string {
|
|
18
|
+
const html = 'text' in token ? token.text : '';
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
return html
|
|
21
|
+
.replace(/&/g, '&')
|
|
22
|
+
.replace(/</g, '<')
|
|
23
|
+
.replace(/>/g, '>');
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/* --------------------------------------------------
|
|
27
|
+
🔐 2. Link sicuri
|
|
28
|
+
-------------------------------------------------- */
|
|
29
|
+
const originalLinkRenderer = renderer.link.bind(renderer);
|
|
30
|
+
|
|
31
|
+
const dangerousProtocols = [
|
|
32
|
+
/^javascript:/i,
|
|
33
|
+
/^data:/i,
|
|
34
|
+
/^vbscript:/i
|
|
35
|
+
];
|
|
17
36
|
|
|
18
|
-
|
|
37
|
+
renderer.link = function({ href, title, tokens }: any) {
|
|
19
38
|
|
|
20
|
-
|
|
21
|
-
🔐 1. NON renderizzare HTML raw
|
|
22
|
-
-------------------------------------------------- */
|
|
23
|
-
renderer.html = function(token: Tokens.HTML | Tokens.Tag): string {
|
|
24
|
-
const html = 'text' in token ? token.text : '';
|
|
39
|
+
const normalized = (href || '').trim();
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.replace(/>/g, '>');
|
|
30
|
-
};
|
|
41
|
+
const isDangerous = dangerousProtocols.some(pattern =>
|
|
42
|
+
pattern.test(normalized)
|
|
43
|
+
);
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const originalLinkRenderer = renderer.link.bind(renderer);
|
|
45
|
+
if (isDangerous) {
|
|
46
|
+
return tokens ? tokens.map((t: any) => t.raw).join('') : href || '';
|
|
47
|
+
}
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
/^javascript:/i,
|
|
39
|
-
/^data:/i,
|
|
40
|
-
/^vbscript:/i
|
|
41
|
-
];
|
|
49
|
+
const html = originalLinkRenderer({ href, title, tokens });
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
// aggiunge sicurezza ai link
|
|
52
|
+
return html.replace(
|
|
53
|
+
'<a ',
|
|
54
|
+
'<a target="_blank" rel="noopener noreferrer" '
|
|
55
|
+
);
|
|
56
|
+
};
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
MarkedPipe.renderer = renderer;
|
|
59
|
+
}
|
|
60
|
+
return MarkedPipe.renderer;
|
|
61
|
+
}
|
|
46
62
|
|
|
47
|
-
|
|
48
|
-
pattern.test(normalized)
|
|
49
|
-
);
|
|
63
|
+
transform(value: any): string {
|
|
50
64
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
const input =
|
|
66
|
+
typeof value === 'string'
|
|
67
|
+
? value
|
|
68
|
+
: (value === null || value === undefined) ? '' : String(value);
|
|
54
69
|
|
|
55
|
-
|
|
70
|
+
const inputWithNewlines = input.replace(/\\n/g, '\n');
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
return html.replace(
|
|
59
|
-
'<a ',
|
|
60
|
-
'<a target="_blank" rel="noopener noreferrer" '
|
|
61
|
-
);
|
|
62
|
-
};
|
|
72
|
+
const renderer = MarkedPipe.getRenderer();
|
|
63
73
|
|
|
64
74
|
marked.setOptions({
|
|
65
75
|
renderer,
|
|
@@ -66,6 +66,8 @@ export class GlobalSettingsService {
|
|
|
66
66
|
this.globals.logLevel = this.appConfigService.getConfig().logLevel
|
|
67
67
|
/**SET PERSISTENCE parameter */
|
|
68
68
|
this.globals.persistence = this.appConfigService.getConfig().authPersistence
|
|
69
|
+
/**SET CLOSE CHAT IN CONVERSATION parameter */
|
|
70
|
+
this.globals.closeChatInConversation = this.appConfigService.getConfig().closeChatInConversation;
|
|
69
71
|
|
|
70
72
|
// ------------------------------- //
|
|
71
73
|
/** LOAD PARAMETERS FROM SERVER
|
|
@@ -596,6 +598,9 @@ export class GlobalSettingsService {
|
|
|
596
598
|
if (variables.hasOwnProperty('allowedUploadExtentions')) {
|
|
597
599
|
globals['fileUploadAccept'] = variables['allowedUploadExtentions'];
|
|
598
600
|
}
|
|
601
|
+
if(variables.hasOwnProperty('showAudioStreamFooterButton')) {
|
|
602
|
+
globals['showAudioStreamFooterButton'] = variables['showAudioStreamFooterButton'];
|
|
603
|
+
}
|
|
599
604
|
|
|
600
605
|
}
|
|
601
606
|
}
|
|
@@ -947,6 +952,14 @@ export class GlobalSettingsService {
|
|
|
947
952
|
if (TEMP !== undefined) {
|
|
948
953
|
globals.soundEnabled = TEMP;
|
|
949
954
|
}
|
|
955
|
+
TEMP = tiledeskSettings['keyboardSoundVolume'];
|
|
956
|
+
if (TEMP !== undefined) {
|
|
957
|
+
globals.keyboardSoundVolume = +TEMP;
|
|
958
|
+
}
|
|
959
|
+
TEMP = tiledeskSettings['keyboardSoundFile'];
|
|
960
|
+
if (TEMP !== undefined) {
|
|
961
|
+
globals.keyboardSoundFile = TEMP;
|
|
962
|
+
}
|
|
950
963
|
TEMP = tiledeskSettings['openExternalLinkButton'];
|
|
951
964
|
// this.logger.debug('[GLOBAL-SET] setVariablesFromSettings > openExternalLinkButton:: ', TEMP]);
|
|
952
965
|
if (TEMP !== undefined) {
|
|
@@ -1336,6 +1349,14 @@ export class GlobalSettingsService {
|
|
|
1336
1349
|
if (TEMP !== null) {
|
|
1337
1350
|
this.globals.soundEnabled = TEMP;
|
|
1338
1351
|
}
|
|
1352
|
+
TEMP = el.nativeElement.getAttribute('keyboardSoundVolume');
|
|
1353
|
+
if (TEMP !== null) {
|
|
1354
|
+
this.globals.keyboardSoundVolume = +TEMP;
|
|
1355
|
+
}
|
|
1356
|
+
TEMP = el.nativeElement.getAttribute('keyboardSoundFile');
|
|
1357
|
+
if (TEMP !== null) {
|
|
1358
|
+
this.globals.keyboardSoundFile = TEMP;
|
|
1359
|
+
}
|
|
1339
1360
|
TEMP = el.nativeElement.getAttribute('openExternalLinkButton');
|
|
1340
1361
|
if (TEMP !== null) {
|
|
1341
1362
|
this.globals.openExternalLinkButton = TEMP;
|
|
@@ -1735,6 +1756,16 @@ export class GlobalSettingsService {
|
|
|
1735
1756
|
globals.soundEnabled = stringToBoolean(TEMP);
|
|
1736
1757
|
}
|
|
1737
1758
|
|
|
1759
|
+
TEMP = getParameterByName(windowContext, 'tiledesk_keyboardSoundVolume');
|
|
1760
|
+
if (TEMP) {
|
|
1761
|
+
globals.keyboardSoundVolume = +TEMP;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
TEMP = getParameterByName(windowContext, 'tiledesk_keyboardSoundFile');
|
|
1765
|
+
if (TEMP) {
|
|
1766
|
+
globals.keyboardSoundFile = TEMP;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1738
1769
|
TEMP = getParameterByName(windowContext, 'tiledesk_openExternalLinkButton');
|
|
1739
1770
|
if (TEMP) {
|
|
1740
1771
|
globals.openExternalLinkButton = stringToBoolean(TEMP);
|