@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.
Files changed (21) hide show
  1. package/fesm2022/dataclouder-ngx-agent-cards.mjs +410 -650
  2. package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
  3. package/lib/components/chat-container/chat-container.component.d.ts +3 -18
  4. package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +4 -2
  5. package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +5 -28
  6. package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +1 -1
  7. package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +2 -4
  8. package/lib/components/chat-container/chat-messages-list/message-orchestrator/message-orchestrator.component.d.ts +26 -0
  9. package/lib/components/icons/icons.component.d.ts +1 -1
  10. package/lib/components/{standalone-audio-text-sync/standalone-audio-text-sync.component.d.ts → text-highlighter/text-highlighter.d.ts} +10 -10
  11. package/lib/models/agent.models.d.ts +3 -7
  12. package/lib/models/conversation-ai.class.d.ts +2 -2
  13. package/lib/models/user-data-exchange.d.ts +2 -0
  14. package/lib/pipes/safe-json.pipe.d.ts +15 -0
  15. package/lib/services/conversation.service.d.ts +8 -13
  16. package/lib/services/dc-conversation-builder.service.d.ts +0 -2
  17. package/lib/services/message-processing.service.d.ts +2 -2
  18. package/package.json +1 -1
  19. package/public-api.d.ts +3 -1
  20. package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +0 -33
  21. 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, DestroyRef, computed, ChangeDetectorRef, effect, ChangeDetectionStrategy, Pipe, ViewChild, ViewChildren } from '@angular/core';
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 i2$4 from 'primeng/dialog';
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 { BehaviorSubject, Subject, fromEvent, filter } from 'rxjs';
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$5 from 'primeng/card';
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$3 from 'primeng/paginator';
57
+ import * as i1$4 from 'primeng/paginator';
58
58
  import { PaginatorModule } from 'primeng/paginator';
59
- import * as i2$6 from 'primeng/speeddial';
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
- }], ctorParameters: () => [] });
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, mutate = false) {
985
+ processMessage(message, conversationSettings) {
936
986
  const defaultVoice = this.getVoice(conversationSettings.voice);
937
987
  let processedMessage;
938
- if (mutate) {
939
- // Modify the existing message object
940
- message.voice = defaultVoice;
941
- processedMessage = message;
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
- return processedMessage;
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
- getMessages() {
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
- // Initialize conversation
1056
- async initConversation(agentCard, conversationBuilder, parseDict) {
1057
- if (!agentCard?.conversationSettings) {
1058
- throw new Error('Conversation settings are required');
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
- // Update agent card with conversation settings
1065
- const updatedAgentCard = { ...agentCard, conversationSettings };
1066
- this.agentCardSignal.set(updatedAgentCard);
1067
- if (!conversationSettings.messages) {
1068
- throw new Error('conversationSettings.messages is required in proper format to start conversation');
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.processAssistantMessage(firstAssistantMsg, true);
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 (agentCard.conversationSettings.autoStart) {
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: agentCard.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.processAssistantMessage(response);
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
- // Re-initialize with current agent card
1180
- const currentAgentCard = agentCard || this.agentCardSignal();
1181
- if (currentAgentCard) {
1182
- // Note: This would need the conversationBuilder to be injected or passed
1183
- // await this.initConversation(currentAgentCard, conversationBuilder);
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
- }], ctorParameters: () => [] });
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
- this.micFinishedEvent = output();
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.getMessages()();
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
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1340
- 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", micFinishedEvent: "micFinishedEvent" }, 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"] }] }); }
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
- * Stops all syncs and cleans up all resources
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
- // Clear all maps
1457
- this.highlightedWordsSignalMap.clear();
1458
- this.highlightedWords$Map.clear();
1459
- this.cleanup$Map.clear();
1460
- this.activeAudioMap.clear();
1461
- }
1462
- /**
1463
- * Returns the highlighted words signal for a specific message
1464
- * @param messageId The ID of the message
1465
- */
1466
- getHighlightedWordsSignal(messageId) {
1467
- // Create a new signal for this message if it doesn't exist
1468
- if (!this.highlightedWordsSignalMap.has(messageId)) {
1469
- this.highlightedWordsSignalMap.set(messageId, signal([]));
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
- return this.highlightedWordsSignalMap.get(messageId);
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
- * Returns an observable that emits true when a word at a specific index is highlighted for a specific message
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: AudioTextSyncService, decorators: [{
1505
- type: Injectable,
1506
- args: [{
1507
- providedIn: 'root',
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(undefined); // Or input.required<MessageAudio>() if always expected
1528
+ this.message = input.required(); // Or input.required<MessageAudio>() if always expected
1595
1529
  this.highlightedWords = signal([]); // Signal for highlighted words
1596
- this.isLoading = computed(() => this.message()?.isLoading);
1597
- this.shouldPlayAudio = computed(() => {
1598
- debugger;
1599
- const shouldPlay = !!this.message() && this.message()?.shouldPlayAudio;
1600
- return shouldPlay;
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.playAudio = output();
1603
- this.audioCompleted = output();
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?.content || msg?.text || '';
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
- // Ensure audio sync setup happens if audio element exists
1672
- if (this.audioElement) {
1673
- this.setupAudioSync(timestamps); // Pass timestamps
1674
- }
1620
+ this.subcribeToAudioSync(timestamps); // Pass timestamps
1675
1621
  }
1676
1622
  else {
1677
- // No transcription, clear words and potentially stop sync listeners
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
- setupAudioSync(timestamps) {
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: StandaloneAudioTextSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1800
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: StandaloneAudioTextSyncComponent, isStandalone: true, selector: "dc-standalone-audio-text-sync", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <!-- Icon for play/loading status -->\n <div>\n <!-- <span> lo: {{ isLoading() }} tr: {{ hasTranscription() }} play: {{ shouldPlayAudio() }} </span> -->\n </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (isLoading()) {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n\n } @else {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div class=\"text-content\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText()\"></span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:8px}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px}.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)}}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
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 }}&nbsp;</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: StandaloneAudioTextSyncComponent, decorators: [{
1768
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, decorators: [{
1803
1769
  type: Component,
1804
- args: [{ selector: 'dc-standalone-audio-text-sync', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <!-- Icon for play/loading status -->\n <div>\n <!-- <span> lo: {{ isLoading() }} tr: {{ hasTranscription() }} play: {{ shouldPlayAudio() }} </span> -->\n </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (isLoading()) {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n\n } @else {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div class=\"text-content\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText()\"></span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:8px}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px}.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)}}\n"] }]
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 }}&nbsp;</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 MultiMessageContentComponent {
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.isLoading = input(false);
1778
+ this.messageRole = input.required();
1779
+ this.messagesSignal = signal([]);
1811
1780
  this.playAudio = output();
1812
- // Track current playing message index
1813
- this.currentPlayingIndex = -1;
1814
- }
1815
- /**
1816
- * Checks if a message has transcription timestamps
1817
- * @param message The message to check
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
- * Emits the playAudio event with the selected message and starts sequential playback
1825
- * @param message The message to play
1826
- */
1827
- onPlayMessage(message) {
1828
- // Find the index of the message to play
1829
- const index = this.messages().findIndex((msg) => msg === message || msg.messageId === message.messageId);
1830
- if (index >= 0) {
1831
- this.startSequentialPlayback(index);
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
- * Method to start playing the sequence
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 > startIndex) {
1844
- // Reset all messages
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
- * Handle audio completion from child component
1852
- * @param message The message that completed playback
1853
- */
1854
- onAudioCompleted(message) {
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
- else {
1866
- // All messages played or message not found
1867
- this.currentPlayingIndex = -1;
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
- ngOnInit() {
1871
- console.log(this.messages());
1872
- }
1873
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiMessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1874
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: MultiMessageContentComponent, isStandalone: true, selector: "dc-multi-message-content", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "@for (message of messages(); track message) {\n <!-- Message with transcription -->\n @if (hasTranscription(message)) {\n <dc-standalone-audio-text-sync [message]=\"message\" (playAudio)=\"onPlayMessage(message)\" (audioCompleted)=\"onAudioCompleted(message)\"></dc-standalone-audio-text-sync>\n }@else {\n <!-- Message without transcription -->\n\n <div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading(), message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n </div>\n } }\n\n <!-- Icon template for play/loading status -->\n <ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n @if (isLoading) {\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n }\n @if (!isLoading) {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n </ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: StandaloneAudioTextSyncComponent, selector: "dc-standalone-audio-text-sync", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1875
- }
1876
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiMessageContentComponent, decorators: [{
1877
- type: Component,
1878
- args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [IconsComponent, StandaloneAudioTextSyncComponent, CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messages(); track message) {\n <!-- Message with transcription -->\n @if (hasTranscription(message)) {\n <dc-standalone-audio-text-sync [message]=\"message\" (playAudio)=\"onPlayMessage(message)\" (audioCompleted)=\"onAudioCompleted(message)\"></dc-standalone-audio-text-sync>\n }@else {\n <!-- Message without transcription -->\n\n <div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading(), message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n </div>\n } }\n\n <!-- Icon template for play/loading status -->\n <ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n @if (isLoading) {\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n }\n @if (!isLoading) {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n </ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
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
- // Split the original text into an array of words
1895
- const words = originalText.split(' ');
1896
- let currentTime = 0;
1897
- for (const word of words) {
1898
- const lowercaseWord = word.toLowerCase();
1899
- const matchedObjs = transcriptionMap.get(lowercaseWord);
1900
- if (matchedObjs && matchedObjs.length > 0) {
1901
- const matchedObj = matchedObjs.shift();
1902
- result.push({
1903
- word: word,
1904
- start: Number(matchedObj.start.toFixed(3)),
1905
- end: Number(matchedObj.end.toFixed(3)),
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
- else {
1910
- result.push({
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
- return result;
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
- this.destroyRef = inject(DestroyRef);
1949
- this.conversationChatSettings = signal(null);
1950
- this.isLoading = signal(false);
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
- if (settings$.repeatRecording && settings$.synthVoice && message.audioUrl) {
1965
- this.playMessage(message);
1966
- }
1919
+ this.audioMessage.set({ ...message });
1967
1920
  }
1968
- else if (settings$.synthVoice) {
1969
- if (message.multiMessages) {
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.generateAndPlayAudio(message);
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 <span class=\"avatar-initial\">A</span>\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-multi-message-content [messages]=\"multiMessages()\" (playAudio)=\"playMessage($event)\"></dc-multi-message-content>\n } @else {\n <dc-standalone-audio-text-sync [message]=\"audioMessage()\"></dc-standalone-audio-text-sync>\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 <span class=\"avatar-initial\">U</span>\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-reverse}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-right: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}.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: MultiMessageContentComponent, selector: "dc-multi-message-content", inputs: ["messages", "isLoading"], outputs: ["playAudio"] }, { kind: "component", type: StandaloneAudioTextSyncComponent, selector: "dc-standalone-audio-text-sync", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
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, MultiMessageContentComponent, StandaloneAudioTextSyncComponent], 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 <span class=\"avatar-initial\">A</span>\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-multi-message-content [messages]=\"multiMessages()\" (playAudio)=\"playMessage($event)\"></dc-multi-message-content>\n } @else {\n <dc-standalone-audio-text-sync [message]=\"audioMessage()\"></dc-standalone-audio-text-sync>\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 <span class=\"avatar-initial\">U</span>\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-reverse}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-right: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}.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"] }]
2122
- }], ctorParameters: () => [] });
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
- // Create a signal for the filter text
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.getMessages()();
2136
- return allMessages.filter((message) => message.role !== 'system');
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 }, thinkingImg: { classPropertyName: "thinkingImg", publicName: "thinkingImg", 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 }); }
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.evaluationService = inject(EvaluationService);
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
- this.messages = this.conversationService.getMessages();
2373
- this.isThinking = this.conversationService.isThinking();
2374
- this.score = this.evaluationService.getScore();
2175
+ // public messages = this.conversationService.getMessages();
2176
+ this.messages = signal([]);
2375
2177
  }
2376
2178
  async ngOnInit() {
2377
- // Get user settings if not provided
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
- this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
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\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer \n [isAIThinking]=\"isThinking()\" \n [micSettings]=\"micSettings\" \n [evaluatorAgentCard]=\"evaluatorAgentCard()\" \n (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <!-- @if (score() > 0) {\n <div class=\"score-container\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n } -->\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 | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".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: "pipe", type: i1$1.JsonPipe, name: "json" }, { 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", "micFinishedEvent"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "thinkingImg"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2$4.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 }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
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\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer \n [isAIThinking]=\"isThinking()\" \n [micSettings]=\"micSettings\" \n [evaluatorAgentCard]=\"evaluatorAgentCard()\" \n (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <!-- @if (score() > 0) {\n <div class=\"score-container\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n } -->\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 | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".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"] }]
2462
- }], ctorParameters: () => [], propDecorators: { chatUserSettings: [{
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$5.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 }); }
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$6.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$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
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$3.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"] }] }); }
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$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
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, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, ProviderSelectorComponent, StandaloneAudioTextSyncComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, buildObjectTTSRequest, characterCardStringDataDefinition, defaultconvUserSettings, extractAudioAndTranscription, extractJsonFromResponse, matchTranscription, provideChatAIService, provideUserDataExchange };
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