@dataclouder/ngx-agent-cards 0.0.88 → 0.0.89
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/fesm2022/dataclouder-ngx-agent-cards.mjs +410 -650
- package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
- package/lib/components/chat-container/chat-container.component.d.ts +3 -18
- package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +4 -2
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +5 -28
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +1 -1
- package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +2 -4
- package/lib/components/chat-container/chat-messages-list/message-orchestrator/message-orchestrator.component.d.ts +26 -0
- package/lib/components/icons/icons.component.d.ts +1 -1
- package/lib/components/{standalone-audio-text-sync/standalone-audio-text-sync.component.d.ts → text-highlighter/text-highlighter.d.ts} +10 -10
- package/lib/models/agent.models.d.ts +3 -7
- package/lib/models/conversation-ai.class.d.ts +2 -2
- package/lib/models/user-data-exchange.d.ts +2 -0
- package/lib/pipes/safe-json.pipe.d.ts +15 -0
- package/lib/services/conversation.service.d.ts +8 -13
- package/lib/services/dc-conversation-builder.service.d.ts +0 -2
- package/lib/services/message-processing.service.d.ts +2 -2
- package/package.json +1 -1
- package/public-api.d.ts +3 -1
- package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +0 -33
- package/lib/services/audio-text-sync.service.d.ts +0 -57
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, inject, input, output, Input, Component, signal,
|
|
2
|
+
import { InjectionToken, Injectable, inject, Pipe, input, output, Input, Component, signal, computed, ChangeDetectorRef, DestroyRef, effect, ChangeDetectionStrategy, ViewChild, ViewChildren } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { CommonModule, DatePipe, DecimalPipe, NgComponentOutlet } from '@angular/common';
|
|
5
5
|
import { DynamicDialogRef, DialogService, DynamicDialogConfig, DynamicDialogModule } from 'primeng/dynamicdialog';
|
|
6
|
-
import * as
|
|
6
|
+
import * as i1$3 from 'primeng/dialog';
|
|
7
7
|
import { DialogModule } from 'primeng/dialog';
|
|
8
8
|
import * as i2 from 'primeng/progressbar';
|
|
9
9
|
import { ProgressBarModule } from 'primeng/progressbar';
|
|
@@ -14,11 +14,11 @@ import * as i3 from 'primeng/textarea';
|
|
|
14
14
|
import { TextareaModule } from 'primeng/textarea';
|
|
15
15
|
import * as i2$1 from 'primeng/button';
|
|
16
16
|
import { ButtonModule } from 'primeng/button';
|
|
17
|
+
import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
17
18
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
18
|
-
import {
|
|
19
|
+
import { Subject, fromEvent, filter } from 'rxjs';
|
|
19
20
|
import { takeUntil, map } from 'rxjs/operators';
|
|
20
21
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
21
|
-
import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
22
22
|
import * as i1$2 from 'primeng/skeleton';
|
|
23
23
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
24
24
|
import * as i2$3 from 'primeng/checkbox';
|
|
@@ -49,14 +49,14 @@ import * as i9 from 'primeng/select';
|
|
|
49
49
|
import { SelectModule } from 'primeng/select';
|
|
50
50
|
import * as i10 from 'primeng/popover';
|
|
51
51
|
import { PopoverModule } from 'primeng/popover';
|
|
52
|
-
import * as i2$
|
|
52
|
+
import * as i2$4 from 'primeng/card';
|
|
53
53
|
import { CardModule } from 'primeng/card';
|
|
54
54
|
import * as i3$2 from 'primeng/dropdown';
|
|
55
55
|
import { DropdownModule } from 'primeng/dropdown';
|
|
56
56
|
import { ChipModule } from 'primeng/chip';
|
|
57
|
-
import * as i1$
|
|
57
|
+
import * as i1$4 from 'primeng/paginator';
|
|
58
58
|
import { PaginatorModule } from 'primeng/paginator';
|
|
59
|
-
import * as i2$
|
|
59
|
+
import * as i2$5 from 'primeng/speeddial';
|
|
60
60
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
61
61
|
|
|
62
62
|
const characterCardStringDataDefinition = `
|
|
@@ -83,8 +83,6 @@ class WordTimestamps {
|
|
|
83
83
|
}
|
|
84
84
|
class MessageAudio {
|
|
85
85
|
}
|
|
86
|
-
class ChatMultiMessage extends MessageAudio {
|
|
87
|
-
}
|
|
88
86
|
var ChatRole;
|
|
89
87
|
(function (ChatRole) {
|
|
90
88
|
ChatRole["System"] = "system";
|
|
@@ -682,7 +680,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
682
680
|
args: [{
|
|
683
681
|
providedIn: 'root',
|
|
684
682
|
}]
|
|
685
|
-
}]
|
|
683
|
+
}] });
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* SafeJsonPipe - A pipe that safely stringifies objects with circular references
|
|
687
|
+
* This pipe handles circular references and DOM objects that can't be serialized
|
|
688
|
+
*/
|
|
689
|
+
class SafeJsonPipe {
|
|
690
|
+
transform(value) {
|
|
691
|
+
// Create a new object with only serializable properties
|
|
692
|
+
return this.stringifySafely(value);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Safely stringify an object, handling circular references
|
|
696
|
+
*/
|
|
697
|
+
stringifySafely(obj) {
|
|
698
|
+
const seen = new WeakSet();
|
|
699
|
+
return JSON.stringify(obj, (key, value) => {
|
|
700
|
+
// Skip properties that start with underscore (often internal properties)
|
|
701
|
+
if (key.startsWith('_')) {
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
// Handle DOM elements and other non-serializable objects
|
|
705
|
+
if (value instanceof HTMLElement || value instanceof Node) {
|
|
706
|
+
return '[DOM Element]';
|
|
707
|
+
}
|
|
708
|
+
// Handle audio elements specifically
|
|
709
|
+
if (key === 'audioHtml' || key === 'audioElement') {
|
|
710
|
+
return '[Audio Element]';
|
|
711
|
+
}
|
|
712
|
+
// Handle functions
|
|
713
|
+
if (typeof value === 'function') {
|
|
714
|
+
return '[Function]';
|
|
715
|
+
}
|
|
716
|
+
// Handle circular references
|
|
717
|
+
if (typeof value === 'object' && value !== null) {
|
|
718
|
+
if (seen.has(value)) {
|
|
719
|
+
return '[Circular Reference]';
|
|
720
|
+
}
|
|
721
|
+
seen.add(value);
|
|
722
|
+
}
|
|
723
|
+
return value;
|
|
724
|
+
}, 2);
|
|
725
|
+
}
|
|
726
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
727
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, isStandalone: true, name: "safeJson" }); }
|
|
728
|
+
}
|
|
729
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, decorators: [{
|
|
730
|
+
type: Pipe,
|
|
731
|
+
args: [{
|
|
732
|
+
name: 'safeJson',
|
|
733
|
+
standalone: true
|
|
734
|
+
}]
|
|
735
|
+
}] });
|
|
686
736
|
|
|
687
737
|
class ChatHeaderComponent {
|
|
688
738
|
constructor() {
|
|
@@ -932,22 +982,14 @@ function removeAllEmojis(text) {
|
|
|
932
982
|
class MessageProcessingService {
|
|
933
983
|
constructor() { }
|
|
934
984
|
// Process message for display
|
|
935
|
-
processMessage(message, conversationSettings
|
|
985
|
+
processMessage(message, conversationSettings) {
|
|
936
986
|
const defaultVoice = this.getVoice(conversationSettings.voice);
|
|
937
987
|
let processedMessage;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
message.
|
|
941
|
-
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
// Create a new message object
|
|
945
|
-
processedMessage = {
|
|
946
|
-
content: message.content,
|
|
947
|
-
role: message.role,
|
|
948
|
-
voice: defaultVoice
|
|
949
|
-
};
|
|
950
|
-
}
|
|
988
|
+
processedMessage = {
|
|
989
|
+
content: message.content,
|
|
990
|
+
role: message.role,
|
|
991
|
+
voice: defaultVoice,
|
|
992
|
+
};
|
|
951
993
|
// Process based on text engine
|
|
952
994
|
if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
|
|
953
995
|
this.processMultiMessages(processedMessage, conversationSettings);
|
|
@@ -959,7 +1001,8 @@ class MessageProcessingService {
|
|
|
959
1001
|
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
960
1002
|
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
961
1003
|
}
|
|
962
|
-
|
|
1004
|
+
console.log(processedMessage);
|
|
1005
|
+
return { ...message, ...processedMessage };
|
|
963
1006
|
}
|
|
964
1007
|
// Process multi-messages from markdown
|
|
965
1008
|
processMultiMessages(message, settings) {
|
|
@@ -978,7 +1021,7 @@ class MessageProcessingService {
|
|
|
978
1021
|
audioUrl: null,
|
|
979
1022
|
audioPromise: null,
|
|
980
1023
|
text: val.text,
|
|
981
|
-
tag: val.tag
|
|
1024
|
+
tag: val.tag,
|
|
982
1025
|
};
|
|
983
1026
|
});
|
|
984
1027
|
}
|
|
@@ -1022,60 +1065,122 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1022
1065
|
}]
|
|
1023
1066
|
}], ctorParameters: () => [] });
|
|
1024
1067
|
|
|
1068
|
+
function matchTranscription(originalText, transcription) {
|
|
1069
|
+
const result = [];
|
|
1070
|
+
const transcriptionMap = new Map();
|
|
1071
|
+
// Create a map of lowercase words to an array of their corresponding objects in transcription
|
|
1072
|
+
for (const obj of transcription) {
|
|
1073
|
+
const lowercaseWord = obj.word.trim().toLowerCase();
|
|
1074
|
+
if (transcriptionMap.has(lowercaseWord)) {
|
|
1075
|
+
transcriptionMap.get(lowercaseWord).push(obj);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
transcriptionMap.set(lowercaseWord, [obj]);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// Split the original text into an array of words
|
|
1082
|
+
const words = originalText.split(' ');
|
|
1083
|
+
let currentTime = 0;
|
|
1084
|
+
for (const word of words) {
|
|
1085
|
+
const lowercaseWord = word.toLowerCase();
|
|
1086
|
+
const matchedObjs = transcriptionMap.get(lowercaseWord);
|
|
1087
|
+
if (matchedObjs && matchedObjs.length > 0) {
|
|
1088
|
+
const matchedObj = matchedObjs.shift();
|
|
1089
|
+
result.push({
|
|
1090
|
+
word: word,
|
|
1091
|
+
start: Number(matchedObj.start.toFixed(3)),
|
|
1092
|
+
end: Number(matchedObj.end.toFixed(3)),
|
|
1093
|
+
});
|
|
1094
|
+
currentTime = matchedObj.end;
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
result.push({
|
|
1098
|
+
word: word,
|
|
1099
|
+
start: Number(currentTime.toFixed(3)),
|
|
1100
|
+
end: Number(currentTime.toFixed(3)),
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return result;
|
|
1105
|
+
}
|
|
1106
|
+
function extractAudioAndTranscription(message, audio) {
|
|
1107
|
+
message.audioUrl = audio.blobUrl;
|
|
1108
|
+
message.transcription = audio.transcription;
|
|
1109
|
+
if (message.transcription) {
|
|
1110
|
+
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1111
|
+
}
|
|
1112
|
+
return message;
|
|
1113
|
+
}
|
|
1114
|
+
function buildObjectTTSRequest(message, settings = {}) {
|
|
1115
|
+
const generateTranscription = settings?.highlightWords ?? false;
|
|
1116
|
+
const speedRate = settings?.speedRate || 0;
|
|
1117
|
+
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
|
|
1118
|
+
const text = removeEmojis(message.text || message.content);
|
|
1119
|
+
return {
|
|
1120
|
+
text,
|
|
1121
|
+
voice: message.voice || settings.voice,
|
|
1122
|
+
generateTranscription,
|
|
1123
|
+
speedRate,
|
|
1124
|
+
speed,
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1025
1128
|
class ConversationService {
|
|
1026
1129
|
constructor() {
|
|
1130
|
+
// Services
|
|
1027
1131
|
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1028
1132
|
this.messageProcessingService = inject(MessageProcessingService);
|
|
1133
|
+
this.conversationBuilder = inject(DCConversationPromptBuilderService);
|
|
1134
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
1135
|
+
// Signals
|
|
1029
1136
|
this.messagesSignal = signal([]);
|
|
1030
1137
|
this.isThinkingSignal = signal(false);
|
|
1031
1138
|
this.conversationSettingsSignal = signal(null);
|
|
1032
|
-
this.agentCardSignal = signal(null);
|
|
1033
1139
|
this.isDestroyedSignal = signal(false);
|
|
1034
1140
|
}
|
|
1035
1141
|
// Get messages as a signal
|
|
1036
|
-
|
|
1142
|
+
getMessagesSignal() {
|
|
1037
1143
|
return this.messagesSignal;
|
|
1038
1144
|
}
|
|
1145
|
+
// Add message to conversation
|
|
1146
|
+
addMessage(message) {
|
|
1147
|
+
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1148
|
+
}
|
|
1039
1149
|
// Get thinking state as a signal
|
|
1040
1150
|
isThinking() {
|
|
1041
1151
|
return this.isThinkingSignal;
|
|
1042
1152
|
}
|
|
1043
|
-
// Get conversation settings as a signal
|
|
1044
|
-
getConversationSettings() {
|
|
1045
|
-
return this.conversationSettingsSignal;
|
|
1046
|
-
}
|
|
1047
|
-
// Get agent card as a signal
|
|
1048
|
-
getAgentCard() {
|
|
1049
|
-
return this.agentCardSignal;
|
|
1050
|
-
}
|
|
1051
1153
|
// Set destroyed state
|
|
1052
1154
|
setDestroyed(value) {
|
|
1053
1155
|
this.isDestroyedSignal.set(value);
|
|
1054
1156
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1157
|
+
setupConversationWithAgentCard(agentCard) {
|
|
1158
|
+
const conversationSettings = this.conversationBuilder.buildConversationSettings(agentCard);
|
|
1159
|
+
// set initial ids
|
|
1160
|
+
for (const i in conversationSettings.messages) {
|
|
1161
|
+
conversationSettings.messages[i].messageId = 'msg_' + i;
|
|
1059
1162
|
}
|
|
1060
|
-
this.agentCardSignal.set(agentCard);
|
|
1061
|
-
// Build conversation settings
|
|
1062
|
-
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1063
1163
|
this.conversationSettingsSignal.set(conversationSettings);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
// Set initial messages
|
|
1071
|
-
this.messagesSignal.set(conversationSettings.messages);
|
|
1164
|
+
}
|
|
1165
|
+
// Initialize conversation
|
|
1166
|
+
async initConversation(agentCard) {
|
|
1167
|
+
this.setupConversationWithAgentCard(agentCard);
|
|
1168
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1072
1169
|
// Find first assistant message
|
|
1073
1170
|
const firstAssistantMsg = conversationSettings.messages.find((message) => message.role === ChatRole.Assistant);
|
|
1074
1171
|
if (firstAssistantMsg) {
|
|
1075
1172
|
// Process the first assistant message
|
|
1076
|
-
this.
|
|
1173
|
+
const processedMessage = this.messageProcessingService.processMessage(firstAssistantMsg, this.conversationSettingsSignal());
|
|
1174
|
+
// Find the index of the message with the matching ID
|
|
1175
|
+
const messageIndex = conversationSettings.messages.findIndex((message) => message.messageId === firstAssistantMsg.messageId);
|
|
1176
|
+
// If found, replace the message at that index
|
|
1177
|
+
if (messageIndex !== -1) {
|
|
1178
|
+
conversationSettings.messages[messageIndex] = processedMessage;
|
|
1179
|
+
}
|
|
1180
|
+
this.messagesSignal.set(conversationSettings.messages);
|
|
1181
|
+
// this.addMessage(processedMessage);
|
|
1077
1182
|
}
|
|
1078
|
-
else if (
|
|
1183
|
+
else if (conversationSettings.autoStart) {
|
|
1079
1184
|
// Auto-start conversation if configured
|
|
1080
1185
|
await this.sendCurrentConversation();
|
|
1081
1186
|
}
|
|
@@ -1090,19 +1195,6 @@ class ConversationService {
|
|
|
1090
1195
|
// Set thinking state
|
|
1091
1196
|
this.isThinkingSignal.set(true);
|
|
1092
1197
|
try {
|
|
1093
|
-
if (message.audioUrl && this.getAudioPlaybackSetting()) {
|
|
1094
|
-
// Wait for audio to finish playing before sending response
|
|
1095
|
-
await new Promise((resolve) => {
|
|
1096
|
-
if (message.audioHtml) {
|
|
1097
|
-
message.audioHtml.addEventListener('ended', () => {
|
|
1098
|
-
resolve();
|
|
1099
|
-
});
|
|
1100
|
-
}
|
|
1101
|
-
else {
|
|
1102
|
-
resolve();
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
}
|
|
1106
1198
|
// Send to AI service
|
|
1107
1199
|
await this.sendCurrentConversation();
|
|
1108
1200
|
}
|
|
@@ -1110,18 +1202,7 @@ class ConversationService {
|
|
|
1110
1202
|
this.isThinkingSignal.set(false);
|
|
1111
1203
|
}
|
|
1112
1204
|
}
|
|
1113
|
-
// Add message to conversation
|
|
1114
|
-
addMessage(message) {
|
|
1115
|
-
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1116
|
-
}
|
|
1117
1205
|
// Process assistant message
|
|
1118
|
-
processAssistantMessage(message, mutate = false) {
|
|
1119
|
-
const conversationSettings = this.conversationSettingsSignal();
|
|
1120
|
-
if (!conversationSettings) {
|
|
1121
|
-
throw new Error('Conversation settings not initialized');
|
|
1122
|
-
}
|
|
1123
|
-
return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
|
|
1124
|
-
}
|
|
1125
1206
|
// Send current conversation to AI
|
|
1126
1207
|
async sendCurrentConversation() {
|
|
1127
1208
|
if (this.isDestroyedSignal()) {
|
|
@@ -1129,10 +1210,6 @@ class ConversationService {
|
|
|
1129
1210
|
}
|
|
1130
1211
|
const messages = this.messagesSignal();
|
|
1131
1212
|
const conversationSettings = this.conversationSettingsSignal();
|
|
1132
|
-
const agentCard = this.agentCardSignal();
|
|
1133
|
-
if (!conversationSettings || !agentCard) {
|
|
1134
|
-
throw new Error('Conversation not properly initialized');
|
|
1135
|
-
}
|
|
1136
1213
|
if (messages.length > 31) {
|
|
1137
1214
|
// Safety limit to prevent infinite conversations
|
|
1138
1215
|
return;
|
|
@@ -1140,18 +1217,16 @@ class ConversationService {
|
|
|
1140
1217
|
let conversationMessages = messages;
|
|
1141
1218
|
// Add last prompt if available
|
|
1142
1219
|
if (conversationSettings.last_prompt) {
|
|
1143
|
-
conversationMessages = [
|
|
1144
|
-
...messages,
|
|
1145
|
-
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1146
|
-
];
|
|
1220
|
+
conversationMessages = [...messages, { content: conversationSettings.last_prompt, role: ChatRole.System }];
|
|
1147
1221
|
}
|
|
1148
1222
|
// Prepare conversation DTO
|
|
1149
1223
|
const conversation = {
|
|
1150
1224
|
messages: conversationMessages,
|
|
1151
1225
|
conversationType: conversationSettings.conversationType,
|
|
1152
1226
|
textEngine: conversationSettings.textEngine,
|
|
1153
|
-
model:
|
|
1227
|
+
model: { modelName: '', provider: '' },
|
|
1154
1228
|
};
|
|
1229
|
+
console.warn('fix model name modelName take from user settings');
|
|
1155
1230
|
// Call AI service
|
|
1156
1231
|
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
1157
1232
|
if (!response) {
|
|
@@ -1159,29 +1234,20 @@ class ConversationService {
|
|
|
1159
1234
|
throw new Error('No message returned from AI');
|
|
1160
1235
|
}
|
|
1161
1236
|
// Process response
|
|
1162
|
-
const newMessage = this.
|
|
1237
|
+
const newMessage = this.messageProcessingService.processMessage(response, conversationSettings);
|
|
1163
1238
|
// Add to messages
|
|
1164
1239
|
this.addMessage(newMessage);
|
|
1165
1240
|
this.isThinkingSignal.set(false);
|
|
1166
1241
|
}
|
|
1167
|
-
// Helper to get audio playback setting
|
|
1168
|
-
getAudioPlaybackSetting() {
|
|
1169
|
-
const agentCard = this.agentCardSignal();
|
|
1170
|
-
return agentCard?.conversationSettings?.repeatRecording || false;
|
|
1171
|
-
}
|
|
1172
1242
|
// Reset conversation
|
|
1173
1243
|
async resetConversation(agentCard) {
|
|
1174
|
-
if (agentCard) {
|
|
1175
|
-
this.agentCardSignal.set(agentCard);
|
|
1176
|
-
}
|
|
1177
1244
|
// Clear messages
|
|
1178
1245
|
this.messagesSignal.set([]);
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
}
|
|
1246
|
+
}
|
|
1247
|
+
async getTTSFile(message) {
|
|
1248
|
+
const userChatSettings = await this.userDataExchange.getUserChatSettings();
|
|
1249
|
+
const ttsRequest = buildObjectTTSRequest(message, userChatSettings);
|
|
1250
|
+
return this.agentCardService.getTextAudioFile(ttsRequest);
|
|
1185
1251
|
}
|
|
1186
1252
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1187
1253
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
|
|
@@ -1191,7 +1257,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1191
1257
|
args: [{
|
|
1192
1258
|
providedIn: 'root',
|
|
1193
1259
|
}]
|
|
1194
|
-
}]
|
|
1260
|
+
}] });
|
|
1195
1261
|
|
|
1196
1262
|
function extractJsonFromResponse(content) {
|
|
1197
1263
|
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
@@ -1286,7 +1352,9 @@ class ChatFooterComponent {
|
|
|
1286
1352
|
this.micSettings = input({ useWhisper: true, lang: 'en' });
|
|
1287
1353
|
this.sendMessage = output();
|
|
1288
1354
|
this.textInputChanged = output();
|
|
1289
|
-
|
|
1355
|
+
// readonly micFinishedEvent = output<any>();
|
|
1356
|
+
this.isGettingTranscription = false;
|
|
1357
|
+
this.isUserTalking = false;
|
|
1290
1358
|
this.chatInputControl = new FormControl();
|
|
1291
1359
|
// Get score from evaluation service
|
|
1292
1360
|
this.score = this.evaluationService.getScore();
|
|
@@ -1304,7 +1372,7 @@ class ChatFooterComponent {
|
|
|
1304
1372
|
* @param eventBlob The blob event from the mic component
|
|
1305
1373
|
*/
|
|
1306
1374
|
micFinished(eventBlob) {
|
|
1307
|
-
this.micFinishedEvent.emit(eventBlob);
|
|
1375
|
+
// this.micFinishedEvent.emit(eventBlob);
|
|
1308
1376
|
}
|
|
1309
1377
|
/**
|
|
1310
1378
|
* Sends the user message
|
|
@@ -1331,182 +1399,52 @@ class ChatFooterComponent {
|
|
|
1331
1399
|
* Evaluate conversation using evaluator agent
|
|
1332
1400
|
*/
|
|
1333
1401
|
async evaluateConversation() {
|
|
1334
|
-
const messages = this.conversationService.
|
|
1402
|
+
const messages = this.conversationService.getMessagesSignal()();
|
|
1335
1403
|
if (this.evaluatorAgentCard()) {
|
|
1336
1404
|
await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard());
|
|
1337
1405
|
}
|
|
1338
1406
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
1343
|
-
type: Component,
|
|
1344
|
-
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
|
|
1345
|
-
}] });
|
|
1346
|
-
|
|
1347
|
-
class AudioTextSyncService {
|
|
1348
|
-
constructor() {
|
|
1349
|
-
// Maps to store message-specific signals and observables
|
|
1350
|
-
this.highlightedWordsSignalMap = new Map();
|
|
1351
|
-
this.highlightedWords$Map = new Map();
|
|
1352
|
-
// Maps for cleanup and active audio elements
|
|
1353
|
-
this.cleanup$Map = new Map();
|
|
1354
|
-
this.activeAudioMap = new Map();
|
|
1355
|
-
this.destroyRef = inject(DestroyRef);
|
|
1356
|
-
// Ensure cleanup when service is destroyed
|
|
1357
|
-
this.destroyRef.onDestroy(() => {
|
|
1358
|
-
this.stopAllSyncs();
|
|
1359
|
-
});
|
|
1360
|
-
}
|
|
1361
|
-
/**
|
|
1362
|
-
* Synchronizes audio playback with text transcription
|
|
1363
|
-
* @param audioElement The audio element to sync with
|
|
1364
|
-
* @param transcriptionTimestamps Array of word timestamps
|
|
1365
|
-
* @param messageId Unique identifier for the message
|
|
1366
|
-
*/
|
|
1367
|
-
syncAudioWithText(audioElement, transcriptionTimestamps, messageId) {
|
|
1368
|
-
// Stop any existing sync for this message
|
|
1369
|
-
this.stopSync(messageId);
|
|
1370
|
-
// Create new signal and subject for this message if they don't exist
|
|
1371
|
-
if (!this.highlightedWordsSignalMap.has(messageId)) {
|
|
1372
|
-
this.highlightedWordsSignalMap.set(messageId, signal([]));
|
|
1373
|
-
}
|
|
1374
|
-
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1375
|
-
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1376
|
-
}
|
|
1377
|
-
// Create cleanup subject
|
|
1378
|
-
const cleanup$ = new Subject();
|
|
1379
|
-
this.cleanup$Map.set(messageId, cleanup$);
|
|
1380
|
-
// Store the active audio element
|
|
1381
|
-
this.activeAudioMap.set(messageId, audioElement);
|
|
1382
|
-
// Get the signal and subject for this message
|
|
1383
|
-
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1384
|
-
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1385
|
-
// Initialize the highlighted words state
|
|
1386
|
-
const initialWords = transcriptionTimestamps.map((word, index) => ({
|
|
1387
|
-
word: word.word,
|
|
1388
|
-
index,
|
|
1389
|
-
isHighlighted: false,
|
|
1390
|
-
}));
|
|
1391
|
-
// Update both signal and observable
|
|
1392
|
-
messageSignal.set(initialWords);
|
|
1393
|
-
messageSubject.next(initialWords);
|
|
1394
|
-
// Listen to timeupdate events
|
|
1395
|
-
fromEvent(audioElement, 'timeupdate')
|
|
1396
|
-
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$), map(() => audioElement.currentTime))
|
|
1397
|
-
.subscribe((currentTime) => {
|
|
1398
|
-
const updatedWords = transcriptionTimestamps.map((word, index) => {
|
|
1399
|
-
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1400
|
-
return {
|
|
1401
|
-
word: word.word,
|
|
1402
|
-
index,
|
|
1403
|
-
isHighlighted,
|
|
1404
|
-
};
|
|
1405
|
-
});
|
|
1406
|
-
// Update both signal and observable for this message
|
|
1407
|
-
messageSignal.set(updatedWords);
|
|
1408
|
-
messageSubject.next(updatedWords);
|
|
1409
|
-
});
|
|
1410
|
-
// Listen to ended event for cleanup
|
|
1411
|
-
fromEvent(audioElement, 'ended')
|
|
1412
|
-
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$))
|
|
1413
|
-
.subscribe(() => {
|
|
1414
|
-
// Reset highlighting when audio ends
|
|
1415
|
-
const resetWords = initialWords.map((word) => ({
|
|
1416
|
-
...word,
|
|
1417
|
-
isHighlighted: false,
|
|
1418
|
-
}));
|
|
1419
|
-
messageSignal.set(resetWords);
|
|
1420
|
-
messageSubject.next(resetWords);
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* Stops the sync for a specific message and cleans up resources
|
|
1425
|
-
* @param messageId The ID of the message to stop syncing
|
|
1426
|
-
*/
|
|
1427
|
-
stopSync(messageId) {
|
|
1428
|
-
if (this.activeAudioMap.has(messageId)) {
|
|
1429
|
-
// Get the cleanup subject for this message
|
|
1430
|
-
const cleanup$ = this.cleanup$Map.get(messageId);
|
|
1431
|
-
if (cleanup$) {
|
|
1432
|
-
cleanup$.next();
|
|
1433
|
-
cleanup$.complete();
|
|
1434
|
-
this.cleanup$Map.delete(messageId);
|
|
1435
|
-
}
|
|
1436
|
-
this.activeAudioMap.delete(messageId);
|
|
1437
|
-
// Reset state for this message
|
|
1438
|
-
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1439
|
-
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1440
|
-
if (messageSignal) {
|
|
1441
|
-
messageSignal.set([]);
|
|
1442
|
-
}
|
|
1443
|
-
if (messageSubject) {
|
|
1444
|
-
messageSubject.next([]);
|
|
1445
|
-
}
|
|
1407
|
+
async onMicFinished(eventBlob) {
|
|
1408
|
+
if (!(eventBlob instanceof Blob)) {
|
|
1409
|
+
return;
|
|
1446
1410
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
*/
|
|
1451
|
-
stopAllSyncs() {
|
|
1452
|
-
// Get all message IDs and stop each sync
|
|
1453
|
-
for (const messageId of this.activeAudioMap.keys()) {
|
|
1454
|
-
this.stopSync(messageId);
|
|
1411
|
+
// Check if audio is too small
|
|
1412
|
+
if (eventBlob.size < 10000) {
|
|
1413
|
+
return;
|
|
1455
1414
|
}
|
|
1456
|
-
|
|
1457
|
-
this.
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1415
|
+
this.isUserTalking = false;
|
|
1416
|
+
this.isGettingTranscription = true;
|
|
1417
|
+
try {
|
|
1418
|
+
// Get transcription from audio
|
|
1419
|
+
// const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, {
|
|
1420
|
+
// conversationId: this.agentCard._id,
|
|
1421
|
+
// });
|
|
1422
|
+
// Create message with transcription
|
|
1423
|
+
// const message: ChatMessage = {
|
|
1424
|
+
// content: transcription.text,
|
|
1425
|
+
// role: ChatRole.User,
|
|
1426
|
+
// transcriptionTimestamps: transcription.words,
|
|
1427
|
+
// };
|
|
1428
|
+
const message = {
|
|
1429
|
+
content: '',
|
|
1430
|
+
role: ChatRole.User,
|
|
1431
|
+
audioUrl: URL.createObjectURL(eventBlob),
|
|
1432
|
+
};
|
|
1433
|
+
// Send message to conversation
|
|
1434
|
+
// The evaluation will happen automatically in the conversation service
|
|
1435
|
+
await this.conversationService.sendUserMessage(message);
|
|
1470
1436
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* Returns the highlighted words observable for a specific message
|
|
1475
|
-
* @param messageId The ID of the message
|
|
1476
|
-
*/
|
|
1477
|
-
getHighlightedWords$(messageId) {
|
|
1478
|
-
// Create a new subject for this message if it doesn't exist
|
|
1479
|
-
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1480
|
-
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1437
|
+
finally {
|
|
1438
|
+
this.isGettingTranscription = false;
|
|
1481
1439
|
}
|
|
1482
|
-
return this.highlightedWords$Map.get(messageId).asObservable();
|
|
1483
|
-
}
|
|
1484
|
-
/**
|
|
1485
|
-
* Checks if a word at a specific index is currently highlighted for a specific message
|
|
1486
|
-
* @param messageId The ID of the message
|
|
1487
|
-
* @param index The index of the word to check
|
|
1488
|
-
*/
|
|
1489
|
-
isWordHighlighted(messageId, index) {
|
|
1490
|
-
const messageSignal = this.getHighlightedWordsSignal(messageId);
|
|
1491
|
-
return messageSignal().some((word) => word.index === index && word.isHighlighted);
|
|
1492
1440
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
* @param messageId The ID of the message
|
|
1496
|
-
* @param index The index of the word to observe
|
|
1497
|
-
*/
|
|
1498
|
-
isWordHighlighted$(messageId, index) {
|
|
1499
|
-
return this.getHighlightedWords$(messageId).pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
|
|
1500
|
-
}
|
|
1501
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1502
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, providedIn: 'root' }); }
|
|
1441
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1442
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: { classPropertyName: "isAIThinking", publicName: "isAIThinking", isSignal: true, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, micSettings: { classPropertyName: "micSettings", publicName: "micSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
|
|
1503
1443
|
}
|
|
1504
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1505
|
-
type:
|
|
1506
|
-
args: [{
|
|
1507
|
-
|
|
1508
|
-
}]
|
|
1509
|
-
}], ctorParameters: () => [] });
|
|
1444
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
1445
|
+
type: Component,
|
|
1446
|
+
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
|
|
1447
|
+
}] });
|
|
1510
1448
|
|
|
1511
1449
|
const ICONS = {
|
|
1512
1450
|
chat: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -1585,40 +1523,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1585
1523
|
`, standalone: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }]
|
|
1586
1524
|
}], ctorParameters: () => [] });
|
|
1587
1525
|
|
|
1588
|
-
|
|
1589
|
-
* Standalone component for audio-text synchronization
|
|
1590
|
-
* This component can work independently from the chat component
|
|
1591
|
-
*/
|
|
1592
|
-
class StandaloneAudioTextSyncComponent {
|
|
1526
|
+
class TextHighlighterComponent {
|
|
1593
1527
|
constructor() {
|
|
1594
|
-
this.message = input(
|
|
1528
|
+
this.message = input.required(); // Or input.required<MessageAudio>() if always expected
|
|
1595
1529
|
this.highlightedWords = signal([]); // Signal for highlighted words
|
|
1596
|
-
this.
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1530
|
+
this.isPlaying = signal(false); // Signal for play state
|
|
1531
|
+
// Signals State computed from the input message
|
|
1532
|
+
this.iconState = computed(() => {
|
|
1533
|
+
if (this.isLoading()) {
|
|
1534
|
+
return 'isLoading';
|
|
1535
|
+
}
|
|
1536
|
+
else if (this.isPlaying()) {
|
|
1537
|
+
return 'isPlaying';
|
|
1538
|
+
}
|
|
1539
|
+
else if (this.message()?.audioUrl) {
|
|
1540
|
+
return 'playable';
|
|
1541
|
+
}
|
|
1542
|
+
else {
|
|
1543
|
+
return 'idle';
|
|
1544
|
+
}
|
|
1601
1545
|
});
|
|
1602
|
-
this.
|
|
1603
|
-
this.
|
|
1604
|
-
this.audioElement = null; // Audio element for playback
|
|
1605
|
-
this.destroy$ = new Subject(); // Cleanup subject
|
|
1606
|
-
// Inject services
|
|
1607
|
-
this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
|
|
1608
|
-
this.destroyRef = inject(DestroyRef);
|
|
1546
|
+
this.isLoading = computed(() => this.message()?.isLoading);
|
|
1547
|
+
this.shouldPlayAudio = computed(() => !!this.message() && this.message()?.shouldPlayAudio);
|
|
1609
1548
|
// Computed signal for message text
|
|
1610
1549
|
this.messageText = computed(() => {
|
|
1550
|
+
console.log('Message text computed', this.message());
|
|
1611
1551
|
const msg = this.message(); // Read the input signal
|
|
1612
|
-
return msg?.
|
|
1552
|
+
return msg?.text || msg?.content || 'N/A';
|
|
1613
1553
|
});
|
|
1554
|
+
this.classTag = computed(() => this.message()?.tag);
|
|
1614
1555
|
// Computed signal for transcription availability
|
|
1615
1556
|
this.hasTranscription = computed(() => {
|
|
1616
1557
|
const hasTranscription = !!this.message()?.transcriptionTimestamps && this.message()?.transcriptionTimestamps.length > 0;
|
|
1617
1558
|
return hasTranscription;
|
|
1618
1559
|
});
|
|
1560
|
+
this.playAudio = output();
|
|
1561
|
+
this.audioCompleted = output();
|
|
1562
|
+
this.audioElement = null; // Audio element for playback
|
|
1563
|
+
this.destroy$ = new Subject(); // Cleanup localsubject
|
|
1564
|
+
// Inject services
|
|
1565
|
+
this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
|
|
1566
|
+
this.destroyRef = inject(DestroyRef);
|
|
1619
1567
|
// Effect to react to message changes and re-initialize
|
|
1620
1568
|
effect(() => {
|
|
1621
|
-
debugger;
|
|
1622
1569
|
const currentMsg = this.message(); // Read the input signal
|
|
1623
1570
|
console.log('Input message signal changed:', currentMsg);
|
|
1624
1571
|
if (currentMsg) {
|
|
@@ -1653,30 +1600,27 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1653
1600
|
this.destroy$.complete();
|
|
1654
1601
|
this.cleanupAudio();
|
|
1655
1602
|
}
|
|
1656
|
-
// This method is called by the effect when the message signal changes
|
|
1657
1603
|
initializeBasedOnMessage(msg) {
|
|
1658
1604
|
// Initialize or cleanup audio based on URL presence and change
|
|
1659
1605
|
if (msg.audioUrl && (!this.audioElement || this.audioElement.src !== msg.audioUrl)) {
|
|
1660
1606
|
this.initializeAudio(msg.audioUrl); // Pass URL
|
|
1661
1607
|
}
|
|
1662
1608
|
else if (!msg.audioUrl && this.audioElement) {
|
|
1663
|
-
// Cleanup if URL removed and element exists
|
|
1664
1609
|
this.cleanupAudio();
|
|
1665
1610
|
}
|
|
1611
|
+
else if (!msg.audioUrl) {
|
|
1612
|
+
// No audio URL and no audio element - just log a warning instead of throwing an error
|
|
1613
|
+
console.warn('No audioUrl provided in the message. Audio playback will not be available.');
|
|
1614
|
+
}
|
|
1666
1615
|
// Initialize words and sync based on transcription presence
|
|
1667
1616
|
if (this.hasTranscription()) {
|
|
1668
1617
|
// Use computed signal
|
|
1669
1618
|
const timestamps = msg.transcriptionTimestamps || [];
|
|
1670
1619
|
this.initializeHighlightedWords(timestamps); // Pass timestamps
|
|
1671
|
-
//
|
|
1672
|
-
if (this.audioElement) {
|
|
1673
|
-
this.setupAudioSync(timestamps); // Pass timestamps
|
|
1674
|
-
}
|
|
1620
|
+
this.subcribeToAudioSync(timestamps); // Pass timestamps
|
|
1675
1621
|
}
|
|
1676
1622
|
else {
|
|
1677
|
-
|
|
1678
|
-
this.highlightedWords.set([]);
|
|
1679
|
-
this.destroy$.next(); // Signal existing listeners to stop
|
|
1623
|
+
this.subscribeToEndAudio();
|
|
1680
1624
|
}
|
|
1681
1625
|
}
|
|
1682
1626
|
/**
|
|
@@ -1701,7 +1645,7 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1701
1645
|
/**
|
|
1702
1646
|
* Set up audio synchronization with text
|
|
1703
1647
|
*/
|
|
1704
|
-
|
|
1648
|
+
subcribeToAudioSync(timestamps) {
|
|
1705
1649
|
// Accept timestamps
|
|
1706
1650
|
if (!this.audioElement) {
|
|
1707
1651
|
// Guard against missing audio element
|
|
@@ -1733,6 +1677,8 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1733
1677
|
isHighlighted: false,
|
|
1734
1678
|
}));
|
|
1735
1679
|
this.highlightedWords.set(resetWords);
|
|
1680
|
+
// Set isPlaying to false when audio finishes
|
|
1681
|
+
this.isPlaying.set(false);
|
|
1736
1682
|
// Emit audio completed event with the current message
|
|
1737
1683
|
const currentMsg = this.message();
|
|
1738
1684
|
if (currentMsg) {
|
|
@@ -1742,6 +1688,23 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1742
1688
|
}
|
|
1743
1689
|
});
|
|
1744
1690
|
}
|
|
1691
|
+
subscribeToEndAudio() {
|
|
1692
|
+
if (!this.audioElement) {
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
fromEvent(this.audioElement, 'ended')
|
|
1696
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
|
|
1697
|
+
.subscribe(() => {
|
|
1698
|
+
// Set isPlaying to false when audio finishes
|
|
1699
|
+
this.isPlaying.set(false);
|
|
1700
|
+
// Emit audio completed event with the current message
|
|
1701
|
+
const currentMsg = this.message();
|
|
1702
|
+
if (currentMsg) {
|
|
1703
|
+
currentMsg.shouldPlayAudio = false;
|
|
1704
|
+
this.audioCompleted.emit(currentMsg);
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1745
1708
|
/**
|
|
1746
1709
|
* Clean up audio element and event listeners
|
|
1747
1710
|
*/
|
|
@@ -1763,6 +1726,7 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1763
1726
|
* Play or pause the audio
|
|
1764
1727
|
*/
|
|
1765
1728
|
onPlayMessage() {
|
|
1729
|
+
console.log('Playing message audio', this.message().messageId);
|
|
1766
1730
|
const currentMsg = this.message(); // Read signal
|
|
1767
1731
|
if (this.audioElement) {
|
|
1768
1732
|
if (this.audioElement.paused) {
|
|
@@ -1778,6 +1742,7 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1778
1742
|
// For simplicity, let's re-call initializeAudio here if needed,
|
|
1779
1743
|
// though ideally the effect handles it.
|
|
1780
1744
|
this.initializeAudio(currentMsg.audioUrl); // Ensure it's created if somehow missed
|
|
1745
|
+
// this.initializeBasedOnMessage(currentMsg);
|
|
1781
1746
|
this.startAudioPlayback();
|
|
1782
1747
|
}
|
|
1783
1748
|
else if (currentMsg) {
|
|
@@ -1795,358 +1760,201 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1795
1760
|
this.audioElement.play().catch((error) => {
|
|
1796
1761
|
console.error('Error playing audio:', error);
|
|
1797
1762
|
});
|
|
1763
|
+
this.isPlaying.set(true); // Set play state to true
|
|
1798
1764
|
}
|
|
1799
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1800
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type:
|
|
1765
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1766
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: TextHighlighterComponent, isStandalone: true, selector: "dc-text-highlighter", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <div> </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span> {{ messageText() }}</span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1801
1767
|
}
|
|
1802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1768
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, decorators: [{
|
|
1803
1769
|
type: Component,
|
|
1804
|
-
args: [{ selector: 'dc-
|
|
1770
|
+
args: [{ selector: 'dc-text-highlighter', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <div> </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span> {{ messageText() }}</span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"] }]
|
|
1805
1771
|
}], ctorParameters: () => [] });
|
|
1806
1772
|
|
|
1807
|
-
class
|
|
1773
|
+
class MessageOrchestratorComponent {
|
|
1808
1774
|
constructor() {
|
|
1775
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1776
|
+
this.conversationService = inject(ConversationService);
|
|
1809
1777
|
this.messages = input.required();
|
|
1810
|
-
this.
|
|
1778
|
+
this.messageRole = input.required();
|
|
1779
|
+
this.messagesSignal = signal([]);
|
|
1811
1780
|
this.playAudio = output();
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
*/
|
|
1819
|
-
hasTranscription(message) {
|
|
1820
|
-
//
|
|
1821
|
-
return !!message.transcriptionTimestamps;
|
|
1781
|
+
this.audioCompleted = output();
|
|
1782
|
+
// Audio queue management
|
|
1783
|
+
this.audioQueue = [];
|
|
1784
|
+
this.isGenerating = false;
|
|
1785
|
+
this.currentPlayingIndex = null;
|
|
1786
|
+
this.preGenerationInProgress = false;
|
|
1822
1787
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1788
|
+
ngOnInit() {
|
|
1789
|
+
if (this.messageRole() === ChatRole.Assistant) {
|
|
1790
|
+
console.log('MessageOrchestratorComponent initialized', this.messages());
|
|
1791
|
+
// Initialize the queue with all message indices
|
|
1792
|
+
this.initializeAudioQueue();
|
|
1793
|
+
// Start processing the queue - generate the first audio
|
|
1794
|
+
this.processNextInQueue();
|
|
1795
|
+
}
|
|
1796
|
+
else {
|
|
1797
|
+
// since user only have one message, just activate signal
|
|
1798
|
+
this.changeStates(0, this.messages()[0]);
|
|
1832
1799
|
}
|
|
1833
|
-
// Also emit to parent if needed
|
|
1834
|
-
this.playAudio.emit(message);
|
|
1835
1800
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
* @param startIndex Index of the message to start playing from
|
|
1839
|
-
*/
|
|
1840
|
-
startSequentialPlayback(startIndex = 0) {
|
|
1841
|
-
this.currentPlayingIndex = startIndex;
|
|
1801
|
+
// Initialize the queue with all message indices
|
|
1802
|
+
initializeAudioQueue() {
|
|
1842
1803
|
const messages = this.messages();
|
|
1843
|
-
if (messages && messages.length >
|
|
1844
|
-
|
|
1845
|
-
messages.forEach((msg) => (msg.shouldPlayAudio = false));
|
|
1846
|
-
// Set the current message to play
|
|
1847
|
-
messages[startIndex].shouldPlayAudio = true;
|
|
1804
|
+
if (messages && messages.length > 0) {
|
|
1805
|
+
this.audioQueue = messages.map((_, index) => index);
|
|
1848
1806
|
}
|
|
1849
1807
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
// Find the index of the completed message
|
|
1856
|
-
const completedIndex = this.messages().findIndex((msg) => msg === message || msg.messageId === message.messageId);
|
|
1857
|
-
if (completedIndex >= 0 && completedIndex < this.messages().length - 1) {
|
|
1858
|
-
// Reset current message
|
|
1859
|
-
const messages = this.messages();
|
|
1860
|
-
messages[completedIndex].shouldPlayAudio = false;
|
|
1861
|
-
// Play the next message
|
|
1862
|
-
this.currentPlayingIndex = completedIndex + 1;
|
|
1863
|
-
messages[this.currentPlayingIndex].shouldPlayAudio = true;
|
|
1808
|
+
// Process the next message in the queue
|
|
1809
|
+
async processNextInQueue() {
|
|
1810
|
+
console.log('Processing next message in queue', this.audioQueue);
|
|
1811
|
+
if (this.audioQueue.length === 0 || this.isGenerating) {
|
|
1812
|
+
return;
|
|
1864
1813
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1814
|
+
// Get the next index from the queue
|
|
1815
|
+
const nextIndex = this.audioQueue.shift();
|
|
1816
|
+
// Start generating
|
|
1817
|
+
this.isGenerating = true;
|
|
1818
|
+
// Generate audio for the current message
|
|
1819
|
+
await this.generateAudioForIndex(nextIndex);
|
|
1820
|
+
// After generation, mark as not generating
|
|
1821
|
+
this.isGenerating = false;
|
|
1822
|
+
// If there's another message in the queue, pre-generate it
|
|
1823
|
+
this.preGenerateNextIfNeeded();
|
|
1824
|
+
}
|
|
1825
|
+
// Pre-generate the next audio if available
|
|
1826
|
+
async preGenerateNextIfNeeded() {
|
|
1827
|
+
if (this.audioQueue.length > 0 && !this.preGenerationInProgress) {
|
|
1828
|
+
const nextIndex = this.audioQueue[0]; // Peek at next index but don't remove
|
|
1829
|
+
// Mark pre-generation as in progress
|
|
1830
|
+
this.preGenerationInProgress = true;
|
|
1831
|
+
// Mark as loading but don't set shouldPlayAudio yet
|
|
1832
|
+
const messages = this.messages();
|
|
1833
|
+
const message = { ...messages[nextIndex], isLoading: true };
|
|
1834
|
+
this.changeStates(nextIndex, message);
|
|
1835
|
+
// Generate in background
|
|
1836
|
+
const messageAudio = await this.generateAudio(messages[nextIndex], null);
|
|
1837
|
+
messageAudio.isLoading = false;
|
|
1838
|
+
messageAudio.shouldPlayAudio = false; // Don't auto-play yet
|
|
1839
|
+
this.changeStates(nextIndex, messageAudio);
|
|
1840
|
+
// Reset pre-generation flag
|
|
1841
|
+
this.preGenerationInProgress = false;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
// Generate audio for a specific index
|
|
1845
|
+
async generateAudioForIndex(index) {
|
|
1846
|
+
const messages = this.messages();
|
|
1847
|
+
// Check if this message was already pre-generated (has audioUrl)
|
|
1848
|
+
if (messages[index].audioUrl) {
|
|
1849
|
+
// If it was pre-generated, just set it to play
|
|
1850
|
+
const messageAudio = { ...messages[index], shouldPlayAudio: true };
|
|
1851
|
+
this.changeStates(index, messageAudio);
|
|
1852
|
+
this.currentPlayingIndex = index;
|
|
1853
|
+
return;
|
|
1868
1854
|
}
|
|
1855
|
+
// Otherwise generate it now
|
|
1856
|
+
const message = { ...messages[index], isLoading: true };
|
|
1857
|
+
this.changeStates(index, message);
|
|
1858
|
+
const messageAudio = await this.generateAudio(messages[index], null);
|
|
1859
|
+
messageAudio.isLoading = false;
|
|
1860
|
+
messageAudio.shouldPlayAudio = true;
|
|
1861
|
+
this.changeStates(index, messageAudio);
|
|
1862
|
+
this.currentPlayingIndex = index;
|
|
1869
1863
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
}], ctorParameters: () => [] });
|
|
1880
|
-
|
|
1881
|
-
function matchTranscription(originalText, transcription) {
|
|
1882
|
-
const result = [];
|
|
1883
|
-
const transcriptionMap = new Map();
|
|
1884
|
-
// Create a map of lowercase words to an array of their corresponding objects in transcription
|
|
1885
|
-
for (const obj of transcription) {
|
|
1886
|
-
const lowercaseWord = obj.word.trim().toLowerCase();
|
|
1887
|
-
if (transcriptionMap.has(lowercaseWord)) {
|
|
1888
|
-
transcriptionMap.get(lowercaseWord).push(obj);
|
|
1889
|
-
}
|
|
1890
|
-
else {
|
|
1891
|
-
transcriptionMap.set(lowercaseWord, [obj]);
|
|
1864
|
+
// Handle audio completion event from text-highlighter
|
|
1865
|
+
onAudioCompleted(message) {
|
|
1866
|
+
// Forward the event
|
|
1867
|
+
this.audioCompleted.emit(message);
|
|
1868
|
+
// Reset current playing index
|
|
1869
|
+
this.currentPlayingIndex = null;
|
|
1870
|
+
// If there are more messages in the queue, process the next one
|
|
1871
|
+
if (this.audioQueue.length > 0) {
|
|
1872
|
+
this.processNextInQueue();
|
|
1892
1873
|
}
|
|
1893
1874
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
});
|
|
1907
|
-
currentTime = matchedObj.end;
|
|
1875
|
+
changeStates(index, messageAudio) {
|
|
1876
|
+
const messages = this.messages();
|
|
1877
|
+
messages[index] = { ...messages[index], ...messageAudio };
|
|
1878
|
+
this.messagesSignal.set([...messages]);
|
|
1879
|
+
}
|
|
1880
|
+
async generateAudio(message, overwriteText = null) {
|
|
1881
|
+
try {
|
|
1882
|
+
const text = overwriteText || message.text || message.content;
|
|
1883
|
+
const ttsObject = buildObjectTTSRequest({ ...message, text }, { highlightWords: true });
|
|
1884
|
+
const speechAudio = await this.conversationService.getTTSFile(ttsObject);
|
|
1885
|
+
message = extractAudioAndTranscription(message, speechAudio);
|
|
1886
|
+
return message;
|
|
1908
1887
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
word: word,
|
|
1912
|
-
start: Number(currentTime.toFixed(3)),
|
|
1913
|
-
end: Number(currentTime.toFixed(3)),
|
|
1914
|
-
});
|
|
1888
|
+
finally {
|
|
1889
|
+
// No longer emitting audioCompleted here as it's handled by the text-highlighter
|
|
1915
1890
|
}
|
|
1916
1891
|
}
|
|
1917
|
-
|
|
1918
|
-
}
|
|
1919
|
-
function extractAudioAndTranscription(message, audio) {
|
|
1920
|
-
message.audioUrl = audio.blobUrl;
|
|
1921
|
-
message.transcription = audio.transcription;
|
|
1922
|
-
if (message.transcription) {
|
|
1923
|
-
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
function buildObjectTTSRequest(message, settings = {}) {
|
|
1927
|
-
const generateTranscription = settings?.highlightWords ?? false;
|
|
1928
|
-
const speedRate = settings?.speedRate || 0;
|
|
1929
|
-
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
|
|
1930
|
-
const text = removeEmojis(message.text || message.content);
|
|
1931
|
-
return {
|
|
1932
|
-
text,
|
|
1933
|
-
voice: message.voice || settings.voice,
|
|
1934
|
-
generateTranscription,
|
|
1935
|
-
speedRate,
|
|
1936
|
-
speed,
|
|
1937
|
-
};
|
|
1892
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1893
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: MessageOrchestratorComponent, isStandalone: true, selector: "dc-message-orchestrator", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, messageRole: { classPropertyName: "messageRole", publicName: "messageRole", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter [message]=\"message\" (playAudio)=\"playAudio.emit($event)\" (audioCompleted)=\"onAudioCompleted($event)\" />\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: TextHighlighterComponent, selector: "dc-text-highlighter", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1938
1894
|
}
|
|
1895
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, decorators: [{
|
|
1896
|
+
type: Component,
|
|
1897
|
+
args: [{ selector: 'dc-message-orchestrator', imports: [TextHighlighterComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter [message]=\"message\" (playAudio)=\"playAudio.emit($event)\" (audioCompleted)=\"onAudioCompleted($event)\" />\n}\n", styles: [":host{display:block}\n"] }]
|
|
1898
|
+
}] });
|
|
1939
1899
|
|
|
1940
1900
|
class ChatMessageComponent {
|
|
1941
1901
|
constructor() {
|
|
1942
|
-
this.audioService = inject(AudioService);
|
|
1943
|
-
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1944
|
-
this.audioTextSyncService = inject(AudioTextSyncService);
|
|
1945
1902
|
this.chatMessage = input.required();
|
|
1946
1903
|
this.chatUserSettings = input(null);
|
|
1947
1904
|
this.audioMessage = signal(null);
|
|
1948
|
-
|
|
1949
|
-
this.
|
|
1950
|
-
this.
|
|
1905
|
+
// Computed properties for easier access to signal values
|
|
1906
|
+
this.hasMultiMessages = computed(() => !!this.chatMessage()?.multiMessages);
|
|
1907
|
+
this.multiMessages = computed(() => this.chatMessage()?.multiMessages || []);
|
|
1908
|
+
this.messageTranslation = computed(() => this.chatMessage()?.translation);
|
|
1909
|
+
this.isUserMessage = computed(() => this.chatMessage()?.role === ChatRole.User);
|
|
1951
1910
|
// Field initializer for the effect - runs during component creation
|
|
1952
1911
|
this.messageEffect = effect(() => {
|
|
1953
1912
|
const message = this.chatMessage();
|
|
1954
1913
|
if (!message)
|
|
1955
1914
|
return;
|
|
1956
|
-
const settings$ = this.conversationChatSettings();
|
|
1957
|
-
// Skip processing if settings aren't loaded yet
|
|
1958
|
-
if (!settings$)
|
|
1959
|
-
return;
|
|
1960
1915
|
if (message.role === ChatRole.AssistantHelper) {
|
|
1961
1916
|
return;
|
|
1962
1917
|
}
|
|
1963
1918
|
if (message.role === ChatRole.User) {
|
|
1964
|
-
|
|
1965
|
-
this.playMessage(message);
|
|
1966
|
-
}
|
|
1919
|
+
this.audioMessage.set({ ...message });
|
|
1967
1920
|
}
|
|
1968
|
-
else if (
|
|
1969
|
-
if (
|
|
1970
|
-
this.generateAndPlayAllAudios(message.multiMessages);
|
|
1921
|
+
else if (message.role === ChatRole.Assistant) {
|
|
1922
|
+
if (this.multiMessages().length > 0) {
|
|
1971
1923
|
}
|
|
1972
1924
|
else {
|
|
1973
|
-
this.
|
|
1925
|
+
this.audioMessage.set({ ...message });
|
|
1974
1926
|
}
|
|
1975
1927
|
}
|
|
1976
1928
|
});
|
|
1977
|
-
// Computed properties for easier access to signal values
|
|
1978
|
-
this.messageRole = computed(() => this.chatMessage()?.role);
|
|
1979
|
-
this.hasMultiMessages = computed(() => !!this.chatMessage()?.multiMessages);
|
|
1980
|
-
this.multiMessages = computed(() => this.chatMessage()?.multiMessages || []);
|
|
1981
|
-
this.messageContent = computed(() => this.chatMessage()?.content || '');
|
|
1982
|
-
this.messageTranslation = computed(() => this.chatMessage()?.translation);
|
|
1983
|
-
this.isUserMessage = computed(() => this.messageRole() === ChatRole.User);
|
|
1984
|
-
this.isAssistantMessage = computed(() => this.messageRole() === ChatRole.Assistant);
|
|
1985
|
-
this.isAssistantHelperMessage = computed(() => this.messageRole() === ChatRole.AssistantHelper);
|
|
1986
|
-
}
|
|
1987
|
-
async ngOnInit() {
|
|
1988
|
-
// Load settings - the effect will react to this change
|
|
1989
|
-
const settings = await this.agentCardService.getConversationUserChatSettings();
|
|
1990
|
-
this.conversationChatSettings.set(settings);
|
|
1991
|
-
// No need for effect here as it's now a field initializer
|
|
1992
|
-
}
|
|
1993
|
-
async generateAndPlayAudio(message, overwriteText = null) {
|
|
1994
|
-
// this.isLoading.set(true);
|
|
1995
|
-
this.audioMessage.set({ ...message, isLoading: true });
|
|
1996
|
-
try {
|
|
1997
|
-
const text = overwriteText || message.content;
|
|
1998
|
-
const ttsObject = buildObjectTTSRequest({ ...message, text }, this.conversationChatSettings());
|
|
1999
|
-
if (message.ssml) {
|
|
2000
|
-
ttsObject.ssml = message.ssml;
|
|
2001
|
-
}
|
|
2002
|
-
const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
|
|
2003
|
-
extractAudioAndTranscription(message, speechAudio);
|
|
2004
|
-
this.audioMessage.set({ ...message, isLoading: false, shouldPlayAudio: true });
|
|
2005
|
-
// this.playMessage(message);
|
|
2006
|
-
}
|
|
2007
|
-
finally {
|
|
2008
|
-
this.isLoading.set(false);
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
playMessage(message) {
|
|
2012
|
-
debugger;
|
|
2013
|
-
if (!message.audioUrl) {
|
|
2014
|
-
return null;
|
|
2015
|
-
}
|
|
2016
|
-
const audioElement = this.audioService.playAudio(message.audioUrl);
|
|
2017
|
-
message['audioHtml'] = audioElement;
|
|
2018
|
-
// console.log('Playing message with transcription:', {
|
|
2019
|
-
// hasTranscription: !!message.transcriptionTimestamps,
|
|
2020
|
-
// transcriptionLength: message.transcriptionTimestamps?.length,
|
|
2021
|
-
// audioUrl: message.audioUrl,
|
|
2022
|
-
// audioElement,
|
|
2023
|
-
// });
|
|
2024
|
-
if (message.transcriptionTimestamps) {
|
|
2025
|
-
// Generate a unique ID for this message
|
|
2026
|
-
const messageId = this.generateMessageId(message);
|
|
2027
|
-
console.log('Syncing audio with text:', {
|
|
2028
|
-
messageId,
|
|
2029
|
-
transcriptionTimestamps: message.transcriptionTimestamps,
|
|
2030
|
-
wordCount: message.transcriptionTimestamps.length,
|
|
2031
|
-
});
|
|
2032
|
-
// Use the audio-text sync service instead of direct manipulation
|
|
2033
|
-
// Pass the messageId to ensure each message has its own state
|
|
2034
|
-
// this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
|
|
2035
|
-
}
|
|
2036
|
-
return audioElement;
|
|
2037
|
-
}
|
|
2038
|
-
/**
|
|
2039
|
-
* Generate a unique ID for a message
|
|
2040
|
-
* Uses message content/text and a timestamp to ensure uniqueness
|
|
2041
|
-
*/
|
|
2042
|
-
generateMessageId(message) {
|
|
2043
|
-
const messageContent = message.content || message.text || '';
|
|
2044
|
-
// Use a safe approach to get an ID-like value since 'id' isn't guaranteed on these types
|
|
2045
|
-
const messageId = message._id || message.id || '';
|
|
2046
|
-
const timestamp = new Date().getTime();
|
|
2047
|
-
return `msg_${messageId}_${messageContent.substring(0, 10).replace(/\s+/g, '_')}_${timestamp}`;
|
|
2048
|
-
}
|
|
2049
|
-
// This method is no longer needed as we're using the AudioTextSyncService
|
|
2050
|
-
// It's kept here as a reference but can be removed
|
|
2051
|
-
/*
|
|
2052
|
-
private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
|
|
2053
|
-
// This functionality has been moved to AudioTextSyncService
|
|
2054
|
-
}
|
|
2055
|
-
*/
|
|
2056
|
-
generateAndPlayAllAudios(multiMessages) {
|
|
2057
|
-
if (!multiMessages.length)
|
|
2058
|
-
return;
|
|
2059
|
-
let currentIndex = 0;
|
|
2060
|
-
const playAudioSequentially = async () => {
|
|
2061
|
-
if (currentIndex >= multiMessages.length) {
|
|
2062
|
-
return;
|
|
2063
|
-
}
|
|
2064
|
-
const currentMessage = multiMessages[currentIndex];
|
|
2065
|
-
currentMessage.isLoading = true;
|
|
2066
|
-
try {
|
|
2067
|
-
// Process current message audio if needed
|
|
2068
|
-
if (!currentMessage.audioUrl) {
|
|
2069
|
-
if (currentMessage.audioPromise) {
|
|
2070
|
-
await currentMessage.audioPromise;
|
|
2071
|
-
}
|
|
2072
|
-
else {
|
|
2073
|
-
const request = buildObjectTTSRequest(currentMessage, this.conversationChatSettings());
|
|
2074
|
-
currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2075
|
-
const audio = await currentMessage.audioPromise;
|
|
2076
|
-
extractAudioAndTranscription(currentMessage, audio);
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
// Play the current message
|
|
2080
|
-
const audioElement = this.playMessage(currentMessage);
|
|
2081
|
-
if (audioElement) {
|
|
2082
|
-
audioElement.addEventListener('ended', () => {
|
|
2083
|
-
currentIndex++;
|
|
2084
|
-
playAudioSequentially();
|
|
2085
|
-
});
|
|
2086
|
-
}
|
|
2087
|
-
else {
|
|
2088
|
-
// If playback failed, move to next message
|
|
2089
|
-
currentIndex++;
|
|
2090
|
-
playAudioSequentially();
|
|
2091
|
-
}
|
|
2092
|
-
// Preload next message audio
|
|
2093
|
-
this.preloadNextMessageAudio(multiMessages, currentIndex);
|
|
2094
|
-
}
|
|
2095
|
-
finally {
|
|
2096
|
-
currentMessage.isLoading = false;
|
|
2097
|
-
}
|
|
2098
|
-
};
|
|
2099
|
-
// Start the sequential playback
|
|
2100
|
-
playAudioSequentially();
|
|
2101
|
-
}
|
|
2102
|
-
preloadNextMessageAudio(messages, currentIndex) {
|
|
2103
|
-
if (currentIndex + 1 < messages.length) {
|
|
2104
|
-
const nextMessage = messages[currentIndex + 1];
|
|
2105
|
-
if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
|
|
2106
|
-
const request = buildObjectTTSRequest(nextMessage, this.conversationChatSettings());
|
|
2107
|
-
nextMessage.isLoading = true;
|
|
2108
|
-
nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2109
|
-
nextMessage.audioPromise.then((audio) => {
|
|
2110
|
-
extractAudioAndTranscription(nextMessage, audio);
|
|
2111
|
-
nextMessage.isLoading = false;
|
|
2112
|
-
});
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
1929
|
}
|
|
2116
1930
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2117
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: { classPropertyName: "chatMessage", publicName: "chatMessage", isSignal: true, isRequired: true, transformFunction: null }, chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <
|
|
1931
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: { classPropertyName: "chatMessage", publicName: "chatMessage", isSignal: true, isRequired: true, transformFunction: null }, chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-message-orchestrator [messages]=\"multiMessages()\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n } @else {\n <dc-message-orchestrator [messages]=\"[audioMessage()]\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-left:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px;overflow:hidden}.avatar-image{width:100%;height:100%;object-fit:cover}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageOrchestratorComponent, selector: "dc-message-orchestrator", inputs: ["messages", "messageRole"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2118
1932
|
}
|
|
2119
1933
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
2120
1934
|
type: Component,
|
|
2121
|
-
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule,
|
|
2122
|
-
}]
|
|
1935
|
+
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MessageOrchestratorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-message-orchestrator [messages]=\"multiMessages()\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n } @else {\n <dc-message-orchestrator [messages]=\"[audioMessage()]\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-left:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px;overflow:hidden}.avatar-image{width:100%;height:100%;object-fit:cover}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"] }]
|
|
1936
|
+
}] });
|
|
2123
1937
|
|
|
2124
1938
|
class ChatMessagesListComponent {
|
|
2125
1939
|
constructor() {
|
|
2126
1940
|
this.chatUserSettings = input.required();
|
|
2127
|
-
this.thinkingImg = input.required();
|
|
2128
1941
|
this.aiIcon = 'assets/default/ai.png';
|
|
2129
1942
|
this.conversationService = inject(ConversationService);
|
|
2130
|
-
|
|
2131
|
-
this.filterText = signal('');
|
|
1943
|
+
this.inputMessages = input.required();
|
|
2132
1944
|
// Get messages and thinking state from the conversation service
|
|
2133
1945
|
this.messages = computed(() => {
|
|
2134
1946
|
// Get the actual array of messages from the signal by calling it as a function
|
|
2135
|
-
const allMessages = this.conversationService.
|
|
2136
|
-
return allMessages.filter((message) => message.role !==
|
|
1947
|
+
const allMessages = this.conversationService.getMessagesSignal()();
|
|
1948
|
+
return allMessages.filter((message) => message.role !== ChatRole.System);
|
|
2137
1949
|
});
|
|
2138
1950
|
this.isThinking = this.conversationService.isThinking();
|
|
2139
1951
|
}
|
|
2140
|
-
// Method to update the filter text
|
|
2141
|
-
updateFilter(value) {
|
|
2142
|
-
this.filterText.set(value);
|
|
2143
|
-
}
|
|
2144
1952
|
// Track messages by their content and role for efficient rendering
|
|
2145
1953
|
trackByMessage(index, message) {
|
|
2146
1954
|
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
2147
1955
|
}
|
|
2148
1956
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2149
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null },
|
|
1957
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null }, inputMessages: { classPropertyName: "inputMessages", publicName: "inputMessages", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2150
1958
|
}
|
|
2151
1959
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
2152
1960
|
type: Component,
|
|
@@ -2351,77 +2159,29 @@ const DefaultEvaluatorAgentCard = {
|
|
|
2351
2159
|
|
|
2352
2160
|
class DCChatComponent {
|
|
2353
2161
|
constructor() {
|
|
2354
|
-
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
2355
2162
|
this.conversationBuilder = inject(DCConversationPromptBuilderService);
|
|
2356
2163
|
this.dialogService = inject(DialogService);
|
|
2357
2164
|
this.conversationService = inject(ConversationService);
|
|
2358
|
-
this.
|
|
2165
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
2166
|
+
this.chatUserSettings = this.userDataExchange.getUserChatSettings(); // Default to user data exchange
|
|
2167
|
+
// TODO: those are optional think if i need them,
|
|
2359
2168
|
this.evaluatorAgentCard = input(DefaultEvaluatorAgentCard);
|
|
2360
2169
|
this.parseDict = input({});
|
|
2361
|
-
this.sendMessage = output();
|
|
2170
|
+
this.sendMessage = output(); // notifies whatever happened inside the chat
|
|
2362
2171
|
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
2363
|
-
this.imageUser = `assets/default/user.svg`;
|
|
2364
|
-
this.thinkingImg = `assets/default/thinking.svg`;
|
|
2365
|
-
this.aiIcon = `assets/default/ai.svg`;
|
|
2366
2172
|
this.isInfoVisible = false;
|
|
2367
|
-
this.isChatSettingsVisible = false;
|
|
2368
|
-
this.isUserTalking = false;
|
|
2369
|
-
this.isGettingTranscription = false;
|
|
2370
2173
|
this.isAdmin = true;
|
|
2371
2174
|
// Get reactive state from services
|
|
2372
|
-
|
|
2373
|
-
this.
|
|
2374
|
-
this.score = this.evaluationService.getScore();
|
|
2175
|
+
// public messages = this.conversationService.getMessages();
|
|
2176
|
+
this.messages = signal([]);
|
|
2375
2177
|
}
|
|
2376
2178
|
async ngOnInit() {
|
|
2377
|
-
|
|
2378
|
-
if (!this.chatUserSettings) {
|
|
2379
|
-
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2380
|
-
}
|
|
2381
|
-
// Initialize conversation
|
|
2382
|
-
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict());
|
|
2179
|
+
this.conversationService.initConversation(this.agentCard);
|
|
2383
2180
|
}
|
|
2384
2181
|
ngOnDestroy() {
|
|
2385
2182
|
// Mark conversation as destroyed to prevent async operations
|
|
2386
2183
|
this.conversationService.setDestroyed(true);
|
|
2387
2184
|
}
|
|
2388
|
-
// onUserMessage method has been moved to the chat-footer component
|
|
2389
|
-
/**
|
|
2390
|
-
* Handle microphone input finished
|
|
2391
|
-
*/
|
|
2392
|
-
async onMicFinished(eventBlob) {
|
|
2393
|
-
if (!(eventBlob instanceof Blob)) {
|
|
2394
|
-
if (!this.chatUserSettings.superHearing) {
|
|
2395
|
-
this.isUserTalking = false;
|
|
2396
|
-
}
|
|
2397
|
-
return;
|
|
2398
|
-
}
|
|
2399
|
-
// Check if audio is too small
|
|
2400
|
-
if (eventBlob.size < 10000) {
|
|
2401
|
-
return;
|
|
2402
|
-
}
|
|
2403
|
-
this.isUserTalking = false;
|
|
2404
|
-
this.isGettingTranscription = true;
|
|
2405
|
-
try {
|
|
2406
|
-
// Get transcription from audio
|
|
2407
|
-
const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, {
|
|
2408
|
-
conversationId: this.agentCard._id,
|
|
2409
|
-
});
|
|
2410
|
-
// Create message with transcription
|
|
2411
|
-
const message = {
|
|
2412
|
-
content: transcription.text,
|
|
2413
|
-
role: ChatRole.User,
|
|
2414
|
-
transcriptionTimestamps: transcription.words,
|
|
2415
|
-
};
|
|
2416
|
-
message['audioUrl'] = URL.createObjectURL(eventBlob);
|
|
2417
|
-
// Send message to conversation
|
|
2418
|
-
// The evaluation will happen automatically in the conversation service
|
|
2419
|
-
await this.conversationService.sendUserMessage(message);
|
|
2420
|
-
}
|
|
2421
|
-
finally {
|
|
2422
|
-
this.isGettingTranscription = false;
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
2185
|
/**
|
|
2426
2186
|
* Open chat settings dialog
|
|
2427
2187
|
*/
|
|
@@ -2435,7 +2195,7 @@ class DCChatComponent {
|
|
|
2435
2195
|
closeOnEscape: true,
|
|
2436
2196
|
})
|
|
2437
2197
|
.onClose.subscribe(async () => {
|
|
2438
|
-
|
|
2198
|
+
// TODO: Que hacer cuando cambie las configuraciones del usario?, creo que si existe en exchange no necesito guardarlo y leer desde el servicio.
|
|
2439
2199
|
});
|
|
2440
2200
|
}
|
|
2441
2201
|
/**
|
|
@@ -2454,12 +2214,12 @@ class DCChatComponent {
|
|
|
2454
2214
|
await this.ngOnInit();
|
|
2455
2215
|
}
|
|
2456
2216
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2457
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [
|
|
2217
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [inputMessages]=\"messages()\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [micSettings]=\"micSettings\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"> </dc-chat-footer>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | safeJson }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | safeJson }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}.chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "evaluatorAgentCard", "micSettings"], outputs: ["sendMessage", "textInputChanged"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "inputMessages"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$3.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "pipe", type: SafeJsonPipe, name: "safeJson" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2458
2218
|
}
|
|
2459
2219
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
2460
2220
|
type: Component,
|
|
2461
|
-
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [
|
|
2462
|
-
}],
|
|
2221
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule, SafeJsonPipe], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [inputMessages]=\"messages()\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [micSettings]=\"micSettings\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"> </dc-chat-footer>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | safeJson }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | safeJson }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}.chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
|
|
2222
|
+
}], propDecorators: { chatUserSettings: [{
|
|
2463
2223
|
type: Input
|
|
2464
2224
|
}], agentCard: [{
|
|
2465
2225
|
type: Input
|
|
@@ -2803,7 +2563,7 @@ class AccountPlatformForm {
|
|
|
2803
2563
|
}
|
|
2804
2564
|
}
|
|
2805
2565
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2806
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$
|
|
2566
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i3$2.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2807
2567
|
}
|
|
2808
2568
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, decorators: [{
|
|
2809
2569
|
type: Component,
|
|
@@ -3283,7 +3043,7 @@ class DCConversationCardUIComponent {
|
|
|
3283
3043
|
this.onCardAction.emit({ event: 'delete', card: this.card() });
|
|
3284
3044
|
}
|
|
3285
3045
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3286
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onCardAction: "onCardAction" }, ngImport: i0, template: "<p-card class=\"card-image\">\n @if(showOptions()) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card()?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card().title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: PopoverModule }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$
|
|
3046
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onCardAction: "onCardAction" }, ngImport: i0, template: "<p-card class=\"card-image\">\n @if(showOptions()) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card()?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card().title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: PopoverModule }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$5.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3287
3047
|
}
|
|
3288
3048
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
|
|
3289
3049
|
type: Component,
|
|
@@ -3430,7 +3190,7 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3430
3190
|
}
|
|
3431
3191
|
}
|
|
3432
3192
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3433
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: false, isRequired: false, transformFunction: null }, customCardComponent: { classPropertyName: "customCardComponent", publicName: "customCardComponent", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null }, gridLayout: { classPropertyName: "gridLayout", publicName: "gridLayout", isSignal: true, isRequired: false, transformFunction: null }, getCustomButtons: { classPropertyName: "getCustomButtons", publicName: "getCustomButtons", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions()\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout() }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions() }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$
|
|
3193
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: false, isRequired: false, transformFunction: null }, customCardComponent: { classPropertyName: "customCardComponent", publicName: "customCardComponent", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null }, gridLayout: { classPropertyName: "gridLayout", publicName: "gridLayout", isSignal: true, isRequired: false, transformFunction: null }, getCustomButtons: { classPropertyName: "getCustomButtons", publicName: "getCustomButtons", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions()\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout() }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions() }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$4.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onFilterAction", "onChangeSort"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
3434
3194
|
}
|
|
3435
3195
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, decorators: [{
|
|
3436
3196
|
type: Component,
|
|
@@ -3511,7 +3271,7 @@ class DcAgentCardDetailsComponent {
|
|
|
3511
3271
|
this.cdr.markForCheck();
|
|
3512
3272
|
}
|
|
3513
3273
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3514
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { agentCardId: "agentCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\n <!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$
|
|
3274
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { agentCardId: "agentCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\n <!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
|
|
3515
3275
|
}
|
|
3516
3276
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
|
|
3517
3277
|
type: Component,
|
|
@@ -3529,5 +3289,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
3529
3289
|
* Generated bundle index. Do not edit.
|
|
3530
3290
|
*/
|
|
3531
3291
|
|
|
3532
|
-
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage,
|
|
3292
|
+
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, MessageOrchestratorComponent, ProviderSelectorComponent, TextEngineOptions, TextEngines, TextHighlighterComponent, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, buildObjectTTSRequest, characterCardStringDataDefinition, convertToHTML, defaultconvUserSettings, extractAudioAndTranscription, extractJsonFromResponse, markdownBasicToHTML, markdownToHTML$1 as markdownToHTML, markdownToHTML2, markdownToHtml, matchTranscription, provideChatAIService, provideUserDataExchange, removeAllEmojis, removeEmojis };
|
|
3533
3293
|
//# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map
|