@dataclouder/ngx-agent-cards 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/dataclouder-ngx-agent-cards.mjs +560 -195
- package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
- package/lib/components/chat-container/chat-container.component.d.ts +7 -5
- package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +13 -4
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +1 -0
- package/lib/components/chat-container/chat-messages-list/message-orchestrator/message-orchestrator.component.d.ts +2 -0
- package/lib/components/dc-agent-card-lists/agent-card-default-ui/agent-card-default-ui.component.d.ts +2 -2
- package/lib/components/dc-agent-card-lists/dc-agent-card-lists.component.d.ts +1 -2
- package/lib/components/dc-agent-form/dc-agent-card-form.component.d.ts +0 -1
- package/lib/components/text-highlighter/extract-tags.d.ts +4 -0
- package/lib/components/text-highlighter/text-highlighter.d.ts +19 -24
- package/lib/models/agent.models.d.ts +29 -3
- package/lib/services/agent-user-progress.service.d.ts +17 -0
- package/lib/services/conversation.service.d.ts +25 -2
- package/lib/services/evaluation.service.d.ts +11 -1
- package/lib/services/popup.service.d.ts +41 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, inject, Pipe, input, output, Input, Component,
|
|
2
|
+
import { InjectionToken, Injectable, inject, RendererFactory2, ApplicationRef, Injector, EnvironmentInjector, signal, createComponent, Pipe, input, output, Input, Component, effect, ViewChild, computed, DestroyRef, ChangeDetectionStrategy, ElementRef, ChangeDetectorRef, ViewChildren } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
|
-
import { CommonModule, DatePipe, DecimalPipe, NgComponentOutlet } from '@angular/common';
|
|
4
|
+
import { DOCUMENT, CommonModule, DatePipe, DecimalPipe, NgComponentOutlet } from '@angular/common';
|
|
5
|
+
import { HttpCoreService, AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
5
6
|
import { DynamicDialogRef, DialogService, DynamicDialogConfig, DynamicDialogModule } from 'primeng/dynamicdialog';
|
|
6
7
|
import * as i1$3 from 'primeng/dialog';
|
|
7
8
|
import { DialogModule } from 'primeng/dialog';
|
|
@@ -15,7 +16,6 @@ import { TextareaModule } from 'primeng/textarea';
|
|
|
15
16
|
import * as i2$1 from 'primeng/button';
|
|
16
17
|
import { ButtonModule } from 'primeng/button';
|
|
17
18
|
import { nanoid } from 'nanoid';
|
|
18
|
-
import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
19
19
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
20
20
|
import { Subject, fromEvent, filter } from 'rxjs';
|
|
21
21
|
import { takeUntil, map } from 'rxjs/operators';
|
|
@@ -59,6 +59,8 @@ import * as i1$4 from 'primeng/paginator';
|
|
|
59
59
|
import { PaginatorModule } from 'primeng/paginator';
|
|
60
60
|
import * as i2$5 from 'primeng/speeddial';
|
|
61
61
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
62
|
+
import * as i4$2 from 'primeng/tag';
|
|
63
|
+
import { TagModule } from 'primeng/tag';
|
|
62
64
|
|
|
63
65
|
const characterCardStringDataDefinition = `
|
|
64
66
|
interface CharacterCardData {
|
|
@@ -104,6 +106,23 @@ class ConversationMessagesDTO {
|
|
|
104
106
|
}
|
|
105
107
|
class ConversationDTO {
|
|
106
108
|
}
|
|
109
|
+
// Define the structure for chat events emitted by DCChatComponent
|
|
110
|
+
var ChatEventType;
|
|
111
|
+
(function (ChatEventType) {
|
|
112
|
+
ChatEventType["WordClicked"] = "wordClicked";
|
|
113
|
+
// Add other potential event types here as needed
|
|
114
|
+
// e.g., MessageSent = 'messageSent', SettingsChanged = 'settingsChanged', GoalCompleted = 'goalCompleted'
|
|
115
|
+
})(ChatEventType || (ChatEventType = {}));
|
|
116
|
+
// Enum to define different types of conversation context for evaluation
|
|
117
|
+
var ContextType;
|
|
118
|
+
(function (ContextType) {
|
|
119
|
+
ContextType["CurrentMessageContent"] = "MessageContent";
|
|
120
|
+
ContextType["LastAssistantMessage"] = "LastAssistantMessage";
|
|
121
|
+
ContextType["LastUserMessage"] = "LastUserMessage";
|
|
122
|
+
ContextType["LastDialog"] = "LastDialog";
|
|
123
|
+
ContextType["Last2Dialogs"] = "Last2Dialogs";
|
|
124
|
+
ContextType["AllConversation"] = "AllConversation";
|
|
125
|
+
})(ContextType || (ContextType = {}));
|
|
107
126
|
|
|
108
127
|
const CONVERSATION_AI_TOKEN = new InjectionToken('Conversation Ai Service');
|
|
109
128
|
// abstract-my-service.ts
|
|
@@ -557,7 +576,7 @@ class DCConversationPromptBuilderService {
|
|
|
557
576
|
// conversation will parse values with curren value in parseDcit:
|
|
558
577
|
// Hello {{user}} im {{char}} your are talking {{base}} -> hello jordan im AI bot your are talking spanish
|
|
559
578
|
// important you have to implement userDataExchange to return what you want in parseDict that have sense for your app.
|
|
560
|
-
if (parseDict) {
|
|
579
|
+
if (parseDict && Object.keys(parseDict).length > 0) {
|
|
561
580
|
return parseDict;
|
|
562
581
|
}
|
|
563
582
|
else {
|
|
@@ -688,6 +707,160 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
688
707
|
}]
|
|
689
708
|
}] });
|
|
690
709
|
|
|
710
|
+
class PopupService {
|
|
711
|
+
constructor() {
|
|
712
|
+
this.document = inject(DOCUMENT);
|
|
713
|
+
this.rendererFactory = inject(RendererFactory2);
|
|
714
|
+
this.appRef = inject(ApplicationRef);
|
|
715
|
+
this.injector = inject(Injector);
|
|
716
|
+
this.environmentInjector = inject(EnvironmentInjector);
|
|
717
|
+
this.currentPopupContainer = null;
|
|
718
|
+
this.currentComponentRef = null;
|
|
719
|
+
this.selectedWord = signal(null); // Keep signal for outside click logic
|
|
720
|
+
this.clickListenerUnlisten = null; // For outside click listener
|
|
721
|
+
this.renderer = this.rendererFactory.createRenderer(null, null);
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Shows a popup containing a dynamically created component.
|
|
725
|
+
* @param top Position from the top of the viewport.
|
|
726
|
+
* @param left Position from the left of the viewport.
|
|
727
|
+
* @param componentType The type of component to render inside the popup.
|
|
728
|
+
* @param injector The injector to use for the dynamic component.
|
|
729
|
+
* @param wordData The WordData to pass to the component's input signal.
|
|
730
|
+
*/
|
|
731
|
+
showPopup(// Constrain T
|
|
732
|
+
top, left, componentType, injector, wordData) {
|
|
733
|
+
if (this.currentComponentRef) {
|
|
734
|
+
this.hidePopup(); // Hide existing popup first
|
|
735
|
+
}
|
|
736
|
+
this.selectedWord.set(wordData); // Track associated word data
|
|
737
|
+
// 1. Create the popup container element
|
|
738
|
+
this.currentPopupContainer = this.renderer.createElement('div');
|
|
739
|
+
this.renderer.addClass(this.currentPopupContainer, 'word-options-popup-dynamic'); // Use this class for targeting (e.g., in outside click listener)
|
|
740
|
+
this.renderer.setStyle(this.currentPopupContainer, 'position', 'fixed');
|
|
741
|
+
this.renderer.setStyle(this.currentPopupContainer, 'top', `${top + 5}px`); // Small offset
|
|
742
|
+
this.renderer.setStyle(this.currentPopupContainer, 'left', `${left + 5}px`); // Small offset
|
|
743
|
+
this.renderer.setStyle(this.currentPopupContainer, 'border', '1px solid #ccc');
|
|
744
|
+
this.renderer.setStyle(this.currentPopupContainer, 'background', 'white');
|
|
745
|
+
// Remove padding from container, let the component handle its own padding
|
|
746
|
+
// this.renderer.setStyle(this.currentPopupContainer, 'padding', '8px');
|
|
747
|
+
this.renderer.setStyle(this.currentPopupContainer, 'border-radius', '4px');
|
|
748
|
+
this.renderer.setStyle(this.currentPopupContainer, 'z-index', '1103'); // Or higher if needed
|
|
749
|
+
this.renderer.setStyle(this.currentPopupContainer, 'box-shadow', '0 2px 8px rgba(0,0,0,0.15)');
|
|
750
|
+
// 2. Create the component dynamically
|
|
751
|
+
this.currentComponentRef = createComponent(componentType, {
|
|
752
|
+
environmentInjector: this.environmentInjector, // Use environment injector
|
|
753
|
+
elementInjector: injector, // Pass the custom injector for data/providers
|
|
754
|
+
});
|
|
755
|
+
// 3. Attach the component to the Angular application lifecycle
|
|
756
|
+
this.appRef.attachView(this.currentComponentRef.hostView);
|
|
757
|
+
// 4. Get the component's root DOM element
|
|
758
|
+
const componentElement = this.currentComponentRef.location.nativeElement;
|
|
759
|
+
// 5. Append the component's element to the container
|
|
760
|
+
this.renderer.appendChild(this.currentPopupContainer, componentElement);
|
|
761
|
+
// 6. Append the container to the document body
|
|
762
|
+
this.renderer.appendChild(this.document.body, this.currentPopupContainer);
|
|
763
|
+
// 7. Set input signal *after* component is created and attached
|
|
764
|
+
// Ensure the component instance has the 'wordData' signal input
|
|
765
|
+
if (this.currentComponentRef.instance.wordData && typeof this.currentComponentRef.instance.wordData.set === 'function') {
|
|
766
|
+
this.currentComponentRef.instance.wordData.set(wordData);
|
|
767
|
+
this.currentComponentRef.changeDetectorRef.detectChanges(); // Trigger change detection
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
console.warn('Dynamic component does not have a writable signal input named "wordData".');
|
|
771
|
+
}
|
|
772
|
+
// 8. Add listener for outside clicks *after* popup is shown
|
|
773
|
+
this.addOutsideClickListener();
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Hides the currently displayed popup and destroys the dynamic component.
|
|
777
|
+
*/
|
|
778
|
+
hidePopup() {
|
|
779
|
+
if (this.currentComponentRef) {
|
|
780
|
+
this.appRef.detachView(this.currentComponentRef.hostView);
|
|
781
|
+
this.currentComponentRef.destroy();
|
|
782
|
+
this.currentComponentRef = null;
|
|
783
|
+
}
|
|
784
|
+
if (this.currentPopupContainer && this.document.body.contains(this.currentPopupContainer)) {
|
|
785
|
+
try {
|
|
786
|
+
this.renderer.removeChild(this.document.body, this.currentPopupContainer);
|
|
787
|
+
}
|
|
788
|
+
catch (error) {
|
|
789
|
+
console.warn('Error removing popup container:', error);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
this.currentPopupContainer = null;
|
|
793
|
+
this.selectedWord.set(null); // Clear selected word signal
|
|
794
|
+
this.removeOutsideClickListener(); // Remove listener when popup is hidden
|
|
795
|
+
}
|
|
796
|
+
addOutsideClickListener() {
|
|
797
|
+
// Remove any existing listener first
|
|
798
|
+
this.removeOutsideClickListener();
|
|
799
|
+
// Use setTimeout to ensure the listener is added after the current click event cycle
|
|
800
|
+
setTimeout(() => {
|
|
801
|
+
this.clickListenerUnlisten = this.renderer.listen(this.document, 'click', (event) => {
|
|
802
|
+
const clickedInsidePopup = this.currentPopupContainer?.contains(event.target);
|
|
803
|
+
// Also check if the click target is the word itself (to prevent immediate closing)
|
|
804
|
+
// This requires knowledge of the word element ID structure
|
|
805
|
+
const clickedOnWord = event.target?.closest('[id^="word-"]');
|
|
806
|
+
if (!clickedInsidePopup && !clickedOnWord && this.currentComponentRef) {
|
|
807
|
+
console.log('Outside click detected, hiding popup.');
|
|
808
|
+
this.hidePopup();
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
}, 0);
|
|
812
|
+
}
|
|
813
|
+
removeOutsideClickListener() {
|
|
814
|
+
if (this.clickListenerUnlisten) {
|
|
815
|
+
this.clickListenerUnlisten();
|
|
816
|
+
this.clickListenerUnlisten = null;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
// Optional: Cleanup logic if the service itself is destroyed
|
|
820
|
+
ngOnDestroy() {
|
|
821
|
+
this.hidePopup(); // Ensure cleanup on service destruction (also removes listener)
|
|
822
|
+
}
|
|
823
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
824
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PopupService, providedIn: 'root' }); }
|
|
825
|
+
}
|
|
826
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PopupService, decorators: [{
|
|
827
|
+
type: Injectable,
|
|
828
|
+
args: [{
|
|
829
|
+
providedIn: 'root',
|
|
830
|
+
}]
|
|
831
|
+
}], ctorParameters: () => [] });
|
|
832
|
+
|
|
833
|
+
// Change this on backend if i have more types
|
|
834
|
+
var AgentCardProgressStatus;
|
|
835
|
+
(function (AgentCardProgressStatus) {
|
|
836
|
+
AgentCardProgressStatus["NotStarted"] = "not_started";
|
|
837
|
+
AgentCardProgressStatus["Started"] = "started";
|
|
838
|
+
AgentCardProgressStatus["Completed"] = "completed";
|
|
839
|
+
})(AgentCardProgressStatus || (AgentCardProgressStatus = {}));
|
|
840
|
+
const DefaultAPI = {
|
|
841
|
+
Progress: 'api/agent-cards-progress',
|
|
842
|
+
};
|
|
843
|
+
class AgentUserProgressService {
|
|
844
|
+
constructor() {
|
|
845
|
+
// Services
|
|
846
|
+
this.httpService = inject(HttpCoreService);
|
|
847
|
+
}
|
|
848
|
+
getProgress() {
|
|
849
|
+
return this.httpService.get(DefaultAPI.Progress + '/user');
|
|
850
|
+
}
|
|
851
|
+
saveProgress(progress) {
|
|
852
|
+
return this.httpService.put(DefaultAPI.Progress + '/user', progress);
|
|
853
|
+
}
|
|
854
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentUserProgressService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
855
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentUserProgressService, providedIn: 'root' }); }
|
|
856
|
+
}
|
|
857
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentUserProgressService, decorators: [{
|
|
858
|
+
type: Injectable,
|
|
859
|
+
args: [{
|
|
860
|
+
providedIn: 'root',
|
|
861
|
+
}]
|
|
862
|
+
}] });
|
|
863
|
+
|
|
691
864
|
/**
|
|
692
865
|
* SafeJsonPipe - A pipe that safely stringifies objects with circular references
|
|
693
866
|
* This pipe handles circular references and DOM objects that can't be serialized
|
|
@@ -998,7 +1171,8 @@ class MessageProcessingService {
|
|
|
998
1171
|
processedMessage.imgUrl = extraData.userImg;
|
|
999
1172
|
}
|
|
1000
1173
|
else if (message.role === ChatRole.Assistant && extraData.assistantImg) {
|
|
1001
|
-
|
|
1174
|
+
// TODO i think lessons send voice in voice, but should be in tts
|
|
1175
|
+
const defaultVoice = this.getVoice(conversationSettings?.tts?.voice || conversationSettings.voice);
|
|
1002
1176
|
processedMessage.voice = defaultVoice;
|
|
1003
1177
|
processedMessage.imgUrl = extraData.assistantImg;
|
|
1004
1178
|
}
|
|
@@ -1007,10 +1181,11 @@ class MessageProcessingService {
|
|
|
1007
1181
|
this.processMultiMessages(processedMessage, conversationSettings);
|
|
1008
1182
|
}
|
|
1009
1183
|
else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
|
|
1010
|
-
|
|
1184
|
+
// TODO: check how to add default secondary voice
|
|
1185
|
+
if (!conversationSettings?.tts?.secondaryVoice) {
|
|
1011
1186
|
throw new Error('Secondary voice is required for SSML');
|
|
1012
1187
|
}
|
|
1013
|
-
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
1188
|
+
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.tts.secondaryVoice);
|
|
1014
1189
|
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
1015
1190
|
}
|
|
1016
1191
|
return processedMessage;
|
|
@@ -1056,14 +1231,15 @@ class MessageProcessingService {
|
|
|
1056
1231
|
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1057
1232
|
}
|
|
1058
1233
|
else {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1234
|
+
return voice_value;
|
|
1235
|
+
// voice should exist, there wont be validation.
|
|
1236
|
+
// const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
|
|
1237
|
+
// if (voice) {
|
|
1238
|
+
// return voice.id;
|
|
1239
|
+
// } else {
|
|
1240
|
+
// console.error('Voice not found getting something random', voice_value);
|
|
1241
|
+
// return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
|
|
1242
|
+
// }
|
|
1067
1243
|
}
|
|
1068
1244
|
}
|
|
1069
1245
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
@@ -1133,6 +1309,7 @@ function buildObjectTTSRequest(message, settings = {}) {
|
|
|
1133
1309
|
generateTranscription,
|
|
1134
1310
|
speedRate,
|
|
1135
1311
|
speed,
|
|
1312
|
+
ssml: message?.ssml,
|
|
1136
1313
|
};
|
|
1137
1314
|
}
|
|
1138
1315
|
|
|
@@ -1150,6 +1327,7 @@ class ConversationService {
|
|
|
1150
1327
|
this.isDestroyedSignal = signal(false);
|
|
1151
1328
|
this.micStatusSignal = signal('ready');
|
|
1152
1329
|
this.currentAudioStatus = signal(null);
|
|
1330
|
+
this.wordClickedSignal = signal(null); // Signal for clicked word
|
|
1153
1331
|
// Var State
|
|
1154
1332
|
this.avatarImages = {
|
|
1155
1333
|
userImg: 'assets/defaults/avatar_user.png',
|
|
@@ -1162,6 +1340,20 @@ class ConversationService {
|
|
|
1162
1340
|
getUserSettings() {
|
|
1163
1341
|
return this.userDataExchange.getUserChatSettings();
|
|
1164
1342
|
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Notifies subscribers that a word has been clicked in a message.
|
|
1345
|
+
* @param wordData The data associated with the clicked word.
|
|
1346
|
+
*/
|
|
1347
|
+
notifyWordClicked(wordData) {
|
|
1348
|
+
this.wordClickedSignal.set(wordData);
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Gets the signal that emits when a word is clicked.
|
|
1352
|
+
* @returns A signal emitting WordData or null.
|
|
1353
|
+
*/
|
|
1354
|
+
getWordClickedSignal() {
|
|
1355
|
+
return this.wordClickedSignal;
|
|
1356
|
+
}
|
|
1165
1357
|
// Add message to conversation
|
|
1166
1358
|
addMessage(message) {
|
|
1167
1359
|
this.messagesSignal.update((messages) => [...messages, message]);
|
|
@@ -1193,7 +1385,7 @@ class ConversationService {
|
|
|
1193
1385
|
}
|
|
1194
1386
|
setupConversationWithAgentCard(agentCard, parseDict = null) {
|
|
1195
1387
|
// Set user AI avatar image
|
|
1196
|
-
this.avatarImages.assistantImg = agentCard?.assets
|
|
1388
|
+
this.avatarImages.assistantImg = agentCard?.assets?.image?.url || this.avatarImages.assistantImg;
|
|
1197
1389
|
const conversationSettings = this.conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1198
1390
|
this.conversationSettingsSignal.set(conversationSettings);
|
|
1199
1391
|
}
|
|
@@ -1204,10 +1396,9 @@ class ConversationService {
|
|
|
1204
1396
|
async initConversation() {
|
|
1205
1397
|
// Set user avatar images
|
|
1206
1398
|
this.avatarImages.userImg = this.userDataExchange.getUserDataExchange().imgUrl;
|
|
1207
|
-
console.log(this.avatarImages);
|
|
1208
1399
|
const conversationSettings = this.conversationSettingsSignal();
|
|
1209
1400
|
for (const i in conversationSettings.messages) {
|
|
1210
|
-
conversationSettings.messages[i].messageId =
|
|
1401
|
+
conversationSettings.messages[i].messageId = nanoid();
|
|
1211
1402
|
}
|
|
1212
1403
|
// Find first assistant message
|
|
1213
1404
|
const firstAssistantMsg = conversationSettings?.messages.find((message) => message.role === ChatRole.Assistant);
|
|
@@ -1232,40 +1423,66 @@ class ConversationService {
|
|
|
1232
1423
|
this.setupConversationWithAgentCard(agentCard, parseDict);
|
|
1233
1424
|
await this.initConversation();
|
|
1234
1425
|
}
|
|
1235
|
-
|
|
1426
|
+
/**
|
|
1427
|
+
* Sends a user message, processes it, adds it to the conversation,
|
|
1428
|
+
* and triggers the AI response flow.
|
|
1429
|
+
* @param message The initial ChatMessage object from the user.
|
|
1430
|
+
* @param updateId Optional ID if this message should update an existing one (e.g., transcription added).
|
|
1431
|
+
* @param updateId Optional ID if this message should update an existing one (e.g., transcription added).
|
|
1432
|
+
* @returns A promise resolving to an object containing the user message ID and the assistant message ID (or null if an error occurred).
|
|
1433
|
+
*/
|
|
1236
1434
|
async sendUserMessage(message, updateId = null) {
|
|
1237
1435
|
if (this.isThinkingSignal()) {
|
|
1238
|
-
|
|
1436
|
+
console.warn('AI is already thinking, message dropped.');
|
|
1437
|
+
return null; // Indicate message was not sent due to AI being busy
|
|
1239
1438
|
}
|
|
1439
|
+
let userMessageId;
|
|
1440
|
+
// Add or update the user message
|
|
1240
1441
|
if (updateId) {
|
|
1241
|
-
|
|
1442
|
+
// Ensure the update includes the ID if not already present in message object
|
|
1443
|
+
const messageToUpdate = { ...message, messageId: updateId };
|
|
1444
|
+
const updated = this.updateMessage(updateId, messageToUpdate);
|
|
1445
|
+
if (!updated) {
|
|
1446
|
+
console.error(`Failed to update message with ID: ${updateId}`);
|
|
1447
|
+
// Decide how to handle this - maybe throw an error or return null?
|
|
1448
|
+
return null;
|
|
1449
|
+
}
|
|
1450
|
+
userMessageId = updateId;
|
|
1242
1451
|
}
|
|
1243
1452
|
else {
|
|
1244
|
-
//
|
|
1453
|
+
// Process and add the new message
|
|
1245
1454
|
const processedMessage = this.messageProcessingService.processMessage(message, this.conversationSettingsSignal(), this.avatarImages);
|
|
1455
|
+
// Ensure ID exists (processMessage should handle this, but fallback just in case)
|
|
1456
|
+
processedMessage.messageId = processedMessage.messageId || nanoid();
|
|
1246
1457
|
this.addMessage(processedMessage);
|
|
1458
|
+
userMessageId = processedMessage.messageId;
|
|
1247
1459
|
}
|
|
1248
|
-
// Set thinking state
|
|
1460
|
+
// Set thinking state and get AI response
|
|
1249
1461
|
this.isThinkingSignal.set(true);
|
|
1462
|
+
let assistantMessageId = null;
|
|
1250
1463
|
try {
|
|
1251
|
-
|
|
1252
|
-
|
|
1464
|
+
assistantMessageId = await this.sendCurrentConversation();
|
|
1465
|
+
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
console.error('Error sending conversation to AI:', error);
|
|
1468
|
+
// Handle error appropriately, maybe set a specific state or message
|
|
1253
1469
|
}
|
|
1254
1470
|
finally {
|
|
1255
1471
|
this.isThinkingSignal.set(false);
|
|
1256
1472
|
}
|
|
1473
|
+
return { userMessageId, assistantMessageId };
|
|
1257
1474
|
}
|
|
1258
1475
|
// Process assistant message
|
|
1259
1476
|
// Send current conversation to AI
|
|
1260
1477
|
async sendCurrentConversation() {
|
|
1261
1478
|
if (this.isDestroyedSignal()) {
|
|
1262
|
-
return;
|
|
1479
|
+
return null;
|
|
1263
1480
|
}
|
|
1264
1481
|
const messages = this.messagesSignal();
|
|
1265
1482
|
const conversationSettings = this.conversationSettingsSignal();
|
|
1266
1483
|
if (messages.length > 31) {
|
|
1267
1484
|
// Safety limit to prevent infinite conversations
|
|
1268
|
-
return;
|
|
1485
|
+
return null;
|
|
1269
1486
|
}
|
|
1270
1487
|
let conversationMessages = messages;
|
|
1271
1488
|
// Add last prompt if available
|
|
@@ -1279,6 +1496,7 @@ class ConversationService {
|
|
|
1279
1496
|
textEngine: conversationSettings.textEngine,
|
|
1280
1497
|
model: conversationSettings.model || { modelName: '', provider: '' },
|
|
1281
1498
|
};
|
|
1499
|
+
this.isThinkingSignal.set(true);
|
|
1282
1500
|
// Call AI service
|
|
1283
1501
|
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
1284
1502
|
if (!response) {
|
|
@@ -1290,6 +1508,7 @@ class ConversationService {
|
|
|
1290
1508
|
// Add to messages
|
|
1291
1509
|
this.addMessage(newMessage);
|
|
1292
1510
|
this.isThinkingSignal.set(false);
|
|
1511
|
+
return newMessage.messageId;
|
|
1293
1512
|
}
|
|
1294
1513
|
// Reset conversation
|
|
1295
1514
|
async resetConversation(agentCard) {
|
|
@@ -1312,7 +1531,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1312
1531
|
}] });
|
|
1313
1532
|
|
|
1314
1533
|
function extractJsonFromResponse(content) {
|
|
1315
|
-
|
|
1534
|
+
// Match everything between the first { and last } OR first [ and last ]
|
|
1535
|
+
const jsonMatch = content.match(/(\{[\s\S]*?\}|\[[\s\S]*?\])/);
|
|
1316
1536
|
if (!jsonMatch)
|
|
1317
1537
|
return null;
|
|
1318
1538
|
try {
|
|
@@ -1367,6 +1587,7 @@ This is the conversation history:
|
|
|
1367
1587
|
and give the response in next JSON format.
|
|
1368
1588
|
${evaluator.expectedResponseType}
|
|
1369
1589
|
`;
|
|
1590
|
+
// TODO: evaluations gemini flashcard
|
|
1370
1591
|
// Send evaluation request
|
|
1371
1592
|
const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
|
|
1372
1593
|
// Not sure what strategy use, if works types should be definied in definitions, generarally response will be score, and feedback.
|
|
@@ -1392,67 +1613,111 @@ and give the response in next JSON format.
|
|
|
1392
1613
|
}
|
|
1393
1614
|
return jsonData;
|
|
1394
1615
|
}
|
|
1395
|
-
//
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1616
|
+
// Helper function to get context messages based on type
|
|
1617
|
+
getContextMessages(messages, contextType) {
|
|
1618
|
+
const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
1619
|
+
if (conversationMessages.length === 0) {
|
|
1620
|
+
return [];
|
|
1621
|
+
}
|
|
1622
|
+
switch (contextType) {
|
|
1623
|
+
case ContextType.LastAssistantMessage: {
|
|
1624
|
+
const lastAssistant = conversationMessages
|
|
1625
|
+
.slice()
|
|
1626
|
+
.reverse()
|
|
1627
|
+
.find((m) => m.role === ChatRole.Assistant);
|
|
1628
|
+
return lastAssistant ? [lastAssistant] : [];
|
|
1629
|
+
}
|
|
1630
|
+
case ContextType.LastUserMessage: {
|
|
1631
|
+
const lastUser = conversationMessages
|
|
1632
|
+
.slice()
|
|
1633
|
+
.reverse()
|
|
1634
|
+
.find((m) => m.role === ChatRole.User);
|
|
1635
|
+
return lastUser ? [lastUser] : [];
|
|
1636
|
+
}
|
|
1637
|
+
case ContextType.LastDialog: {
|
|
1638
|
+
const lastUser = conversationMessages
|
|
1639
|
+
.slice()
|
|
1640
|
+
.reverse()
|
|
1641
|
+
.find((m) => m.role === ChatRole.User);
|
|
1642
|
+
const lastAssistant = conversationMessages
|
|
1643
|
+
.slice()
|
|
1644
|
+
.reverse()
|
|
1645
|
+
.find((m) => m.role === ChatRole.Assistant);
|
|
1646
|
+
const dialog = [];
|
|
1647
|
+
if (lastAssistant)
|
|
1648
|
+
dialog.push(lastAssistant);
|
|
1649
|
+
if (lastUser)
|
|
1650
|
+
dialog.push(lastUser);
|
|
1651
|
+
// Sort by timestamp if available, otherwise keep order [Assistant, User] if both exist
|
|
1652
|
+
// Assuming messageId or a timestamp property exists and is comparable
|
|
1653
|
+
// dialog.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0)); // Example sorting
|
|
1654
|
+
return dialog;
|
|
1655
|
+
}
|
|
1656
|
+
case ContextType.Last2Dialogs: {
|
|
1657
|
+
const users = conversationMessages.filter((m) => m.role === ChatRole.User);
|
|
1658
|
+
const assistants = conversationMessages.filter((m) => m.role === ChatRole.Assistant);
|
|
1659
|
+
const lastTwoUsers = users.slice(-2);
|
|
1660
|
+
const lastTwoAssistants = assistants.slice(-2);
|
|
1661
|
+
const dialogs = [...lastTwoAssistants, ...lastTwoUsers];
|
|
1662
|
+
// Sort by timestamp if available
|
|
1663
|
+
// dialogs.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0)); // Example sorting
|
|
1664
|
+
return dialogs;
|
|
1665
|
+
}
|
|
1666
|
+
case ContextType.AllConversation:
|
|
1667
|
+
default:
|
|
1668
|
+
return conversationMessages;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Evaluates a conversation context based on a specific task and attaches the result to a designated message.
|
|
1673
|
+
* @param agentTask The task definition for the evaluation.
|
|
1674
|
+
* @param messageToUpdate The specific ChatMessage object (user or assistant) to attach the evaluation results to.
|
|
1675
|
+
* @param contextType Determines which part of the conversation history to send as context (default: AllConversation).
|
|
1676
|
+
* @returns The JSON result of the evaluation.
|
|
1677
|
+
*/
|
|
1678
|
+
async evaluateWithTask(agentTask, messageToUpdate, // Message to attach the evaluation to
|
|
1679
|
+
contextType = ContextType.AllConversation) {
|
|
1680
|
+
// Fetch the current conversation messages from the service
|
|
1681
|
+
const messages = this.conversationService.getMessagesSignal()();
|
|
1399
1682
|
const requestMessages = [];
|
|
1400
|
-
// Add system prompt if available
|
|
1401
|
-
// Prioritize characterCard system_prompt, then description as fallback
|
|
1402
1683
|
const systemPrompt = agentTask.systemPrompt || 'You are a helpful assistant.'; // Default fallback
|
|
1403
1684
|
requestMessages.push({ role: ChatRole.System, content: systemPrompt });
|
|
1404
|
-
// Add the main task content
|
|
1405
1685
|
let taskContent = agentTask.task;
|
|
1406
1686
|
if (agentTask.expectedResponseType) {
|
|
1407
|
-
// Ensure proper formatting instructions
|
|
1408
1687
|
taskContent += `\n\nPlease provide the response strictly in the following JSON format:\n${agentTask.expectedResponseType}`;
|
|
1409
1688
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
taskContent += `\n\nHere is the conversation history:\n${conversationHistory}`;
|
|
1422
|
-
}
|
|
1423
|
-
let lastUserMessage = null;
|
|
1424
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1425
|
-
// reverse the array to find the last user message
|
|
1426
|
-
if (messages[i].role === ChatRole.User) {
|
|
1427
|
-
lastUserMessage = messages[i];
|
|
1428
|
-
break;
|
|
1689
|
+
if (contextType === ContextType.CurrentMessageContent) {
|
|
1690
|
+
taskContent += `\n\nHere is text: ${messageToUpdate.content}`;
|
|
1691
|
+
}
|
|
1692
|
+
else {
|
|
1693
|
+
// Get context messages based on the specified type
|
|
1694
|
+
const contextMessages = this.getContextMessages(messages, contextType);
|
|
1695
|
+
if (contextMessages.length > 0) {
|
|
1696
|
+
const conversationHistoryString = contextMessages
|
|
1697
|
+
.map((message) => `${message.role}: ${message.content}`) // Format messages
|
|
1698
|
+
.join('\n');
|
|
1699
|
+
taskContent += `\n\nHere is the relevant conversation history:\n${conversationHistoryString}`;
|
|
1429
1700
|
}
|
|
1430
1701
|
}
|
|
1702
|
+
// No longer need to find the last user message here, it's passed in as messageToUpdate
|
|
1431
1703
|
requestMessages.push({ role: ChatRole.User, content: taskContent });
|
|
1432
1704
|
try {
|
|
1433
|
-
// Send evaluation request to the AI service
|
|
1434
|
-
// Pass the model if the service method supports it
|
|
1435
|
-
console.log(requestMessages);
|
|
1436
1705
|
const response = await this.agentCardService.callChatCompletion({ messages: requestMessages, model: agentTask.model });
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1706
|
+
let jsonData = extractJsonFromResponse(response.content);
|
|
1707
|
+
if (jsonData === null) {
|
|
1708
|
+
jsonData = { score: 1, feedback: 'Good job!' };
|
|
1709
|
+
}
|
|
1441
1710
|
this.evaluationResultSignal.set(jsonData);
|
|
1442
1711
|
// Update score if available
|
|
1443
1712
|
if (jsonData.score) {
|
|
1444
|
-
// Assuming a similar scoring logic as evaluateConversation
|
|
1445
1713
|
// Adjust the multiplier or condition if needed for task-based evaluation
|
|
1446
1714
|
if (jsonData.score <= 3) {
|
|
1447
1715
|
this.updateScore(jsonData.score * 10);
|
|
1448
1716
|
}
|
|
1449
1717
|
}
|
|
1450
|
-
//
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
this.conversationService.updateMessage(lastUserMessage.messageId, {
|
|
1454
|
-
evaluation: jsonData, // Add evaluation data to the message
|
|
1455
|
-
});
|
|
1718
|
+
// Update the specific message provided by the caller with the result
|
|
1719
|
+
if (messageToUpdate?.messageId && jsonData) {
|
|
1720
|
+
this.conversationService.updateMessage(messageToUpdate.messageId, { evaluation: jsonData });
|
|
1456
1721
|
}
|
|
1457
1722
|
return jsonData;
|
|
1458
1723
|
}
|
|
@@ -1466,16 +1731,14 @@ and give the response in next JSON format.
|
|
|
1466
1731
|
throw error;
|
|
1467
1732
|
}
|
|
1468
1733
|
}
|
|
1469
|
-
// Update score with limits
|
|
1470
1734
|
updateScore(additionalScore) {
|
|
1471
1735
|
this.scoreSignal.update((currentScore) => {
|
|
1472
1736
|
const newScore = currentScore + additionalScore;
|
|
1473
1737
|
return newScore > 100 ? 100 : newScore;
|
|
1474
1738
|
});
|
|
1475
1739
|
}
|
|
1476
|
-
// Reset score
|
|
1477
1740
|
resetScore() {
|
|
1478
|
-
this.scoreSignal.set(
|
|
1741
|
+
this.scoreSignal.set(0);
|
|
1479
1742
|
}
|
|
1480
1743
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1481
1744
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, providedIn: 'root' }); }
|
|
@@ -1496,8 +1759,9 @@ class ChatFooterComponent {
|
|
|
1496
1759
|
// Inputs
|
|
1497
1760
|
this.isAIThinking = input(false);
|
|
1498
1761
|
// @deprecated in favor of appAgentTask
|
|
1499
|
-
|
|
1500
|
-
this.
|
|
1762
|
+
// readonly evaluatorAgentCard = input<IMiniAgentCard>();
|
|
1763
|
+
this.taskOnUserMessage = input();
|
|
1764
|
+
this.taskOnAssistantMessage = input();
|
|
1501
1765
|
this.micSettings = input({ useWhisper: true, lang: 'en' });
|
|
1502
1766
|
// Outputs
|
|
1503
1767
|
this.sendMessage = output();
|
|
@@ -1555,31 +1819,46 @@ class ChatFooterComponent {
|
|
|
1555
1819
|
return;
|
|
1556
1820
|
}
|
|
1557
1821
|
const text = this.chatInputControl.value;
|
|
1558
|
-
const message = {
|
|
1559
|
-
content: text,
|
|
1560
|
-
role: ChatRole.User,
|
|
1561
|
-
};
|
|
1562
|
-
// Emit the message for parent components that need it
|
|
1563
|
-
// this.sendMessage.emit(message);
|
|
1564
|
-
// Clear the input field
|
|
1822
|
+
const message = { content: text, role: ChatRole.User };
|
|
1565
1823
|
this.chatInputControl.setValue('');
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1824
|
+
const result = await this.conversationService.sendUserMessage(message);
|
|
1825
|
+
const messages = this.conversationService.getMessagesSignal()();
|
|
1826
|
+
if (result.assistantMessageId) {
|
|
1827
|
+
const assistantMessage = messages.find((m) => m.messageId === result.assistantMessageId);
|
|
1828
|
+
this.evaluateConversation(assistantMessage, 'assistant');
|
|
1829
|
+
}
|
|
1830
|
+
if (result?.userMessageId) {
|
|
1831
|
+
// Evaluate the message only if it was successfully sent and we have the user message ID
|
|
1832
|
+
// Find the message using the userMessageId from the result object
|
|
1833
|
+
const messageToEvaluate = messages.find((m) => m.messageId === result.userMessageId);
|
|
1834
|
+
if (messageToEvaluate) {
|
|
1835
|
+
this.evaluateConversation(messageToEvaluate, 'user');
|
|
1836
|
+
}
|
|
1837
|
+
else {
|
|
1838
|
+
// Log the correct ID in the error message
|
|
1839
|
+
console.error(`Could not find message with ID ${result.userMessageId} to evaluate.`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
console.warn('Message sending failed or returned no ID, skipping evaluation.');
|
|
1844
|
+
}
|
|
1570
1845
|
}
|
|
1571
1846
|
/**
|
|
1572
1847
|
* Evaluate conversation using evaluator agent
|
|
1573
1848
|
*/
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1849
|
+
/**
|
|
1850
|
+
* Evaluate a specific message using the evaluator agent task.
|
|
1851
|
+
* @param messageToEvaluate The ChatMessage (typically the user's last message) to evaluate.
|
|
1852
|
+
*/
|
|
1853
|
+
async evaluateConversation(messageToEvaluate, type) {
|
|
1854
|
+
let agentTask;
|
|
1855
|
+
if (type === 'user' && this.taskOnUserMessage()) {
|
|
1856
|
+
agentTask = this.taskOnUserMessage();
|
|
1857
|
+
const data = await this.evaluationService.evaluateWithTask(agentTask, messageToEvaluate);
|
|
1579
1858
|
}
|
|
1580
|
-
if (this.
|
|
1581
|
-
|
|
1582
|
-
|
|
1859
|
+
else if (type === 'assistant' && this.taskOnAssistantMessage()) {
|
|
1860
|
+
agentTask = this.taskOnAssistantMessage();
|
|
1861
|
+
const data = await this.evaluationService.evaluateWithTask(agentTask, messageToEvaluate, ContextType.CurrentMessageContent);
|
|
1583
1862
|
}
|
|
1584
1863
|
}
|
|
1585
1864
|
handleAudioRecorded(event) {
|
|
@@ -1609,7 +1888,8 @@ class ChatFooterComponent {
|
|
|
1609
1888
|
audioUrl: URL.createObjectURL(eventBlob),
|
|
1610
1889
|
isLoading: true,
|
|
1611
1890
|
};
|
|
1612
|
-
|
|
1891
|
+
// Instead of creating message, eneble animation user is spacking
|
|
1892
|
+
// const messageId = this.conversationService.addMessage(message);
|
|
1613
1893
|
// Get transcription from audio
|
|
1614
1894
|
const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, null);
|
|
1615
1895
|
// Create updated message with transcription and isLoading set to false
|
|
@@ -1619,24 +1899,36 @@ class ChatFooterComponent {
|
|
|
1619
1899
|
transcription: transcription || undefined,
|
|
1620
1900
|
isLoading: false,
|
|
1621
1901
|
};
|
|
1622
|
-
// Send message to conversation
|
|
1623
1902
|
// The evaluation will happen automatically in the conversation service
|
|
1624
|
-
await this.conversationService.sendUserMessage(updatedMessage
|
|
1903
|
+
await this.conversationService.sendUserMessage(updatedMessage);
|
|
1625
1904
|
}
|
|
1626
1905
|
finally {
|
|
1627
1906
|
this.isGettingTranscription = false;
|
|
1907
|
+
// console.log(this.isAIThinking());
|
|
1908
|
+
//
|
|
1628
1909
|
}
|
|
1629
1910
|
}
|
|
1630
1911
|
setScore(score) {
|
|
1631
1912
|
this.evaluationService.setScore(100);
|
|
1632
1913
|
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Stops the microphone recording if the component is available.
|
|
1916
|
+
*/
|
|
1917
|
+
stopMic() {
|
|
1918
|
+
if (this.micComponent) {
|
|
1919
|
+
this.micComponent.stopRecording();
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1633
1922
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1634
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.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 },
|
|
1923
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.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 }, taskOnUserMessage: { classPropertyName: "taskOnUserMessage", publicName: "taskOnUserMessage", isSignal: true, isRequired: false, transformFunction: null }, taskOnAssistantMessage: { classPropertyName: "taskOnAssistantMessage", publicName: "taskOnAssistantMessage", isSignal: true, isRequired: false, transformFunction: null }, micSettings: { classPropertyName: "micSettings", publicName: "micSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged" }, viewQueries: [{ propertyName: "micComponent", first: true, predicate: MicVadComponent, descendants: true }], ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <app-mic-vad\n (audioRecorded)=\"handleAudioRecorded($event)\"\n (statusChanged)=\"handleMicStatusChanged($event)\"\n [continueListening]=\"shouldContinueListening()\" />\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 @if(taskOnUserMessage()) {\n <div (click)=\"setScore(100)\">\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n }\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: "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"] }, { kind: "component", type: MicVadComponent, selector: "app-mic-vad", inputs: ["continueListening"], outputs: ["statusChanged", "audioRecorded", "error"] }] }); }
|
|
1635
1924
|
}
|
|
1636
1925
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
1637
1926
|
type: Component,
|
|
1638
|
-
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, TextareaModule, ButtonModule, MicVadComponent], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <app-mic-vad\n (audioRecorded)=\"handleAudioRecorded($event)\"\n (statusChanged)=\"handleMicStatusChanged($event)\"\n [continueListening]=\"shouldContinueListening()\" />\n
|
|
1639
|
-
}], ctorParameters: () => []
|
|
1927
|
+
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, TextareaModule, ButtonModule, MicVadComponent], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <app-mic-vad\n (audioRecorded)=\"handleAudioRecorded($event)\"\n (statusChanged)=\"handleMicStatusChanged($event)\"\n [continueListening]=\"shouldContinueListening()\" />\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 @if(taskOnUserMessage()) {\n <div (click)=\"setScore(100)\">\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n }\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"] }]
|
|
1928
|
+
}], ctorParameters: () => [], propDecorators: { micComponent: [{
|
|
1929
|
+
type: ViewChild,
|
|
1930
|
+
args: [MicVadComponent]
|
|
1931
|
+
}] } });
|
|
1640
1932
|
|
|
1641
1933
|
const ICONS = {
|
|
1642
1934
|
chat: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -1715,12 +2007,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1715
2007
|
`, 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"] }]
|
|
1716
2008
|
}], ctorParameters: () => [] });
|
|
1717
2009
|
|
|
2010
|
+
// Given a text that can be in markdown but only in asterisk like *italic* or **bold** or ***bold italic***
|
|
2011
|
+
// Example Hola que tal es *Hey What's up* en inglés
|
|
2012
|
+
// i want to extract in array every word like this.
|
|
2013
|
+
// [{word: 'Hola', tag: ''}, {word: 'que', tag: ''}, {word: 'tal', tag: ''}, {word: 'es', tag: ''}, {word: 'Hey', tag: 'italic'}, {word: 'What's', tag: 'italic'}, {word: 'up', tag: 'italic'}, {word: 'en', tag: ''}, {word: 'inglés', tag: ''}]
|
|
2014
|
+
function extractTags(text) {
|
|
2015
|
+
const result = [];
|
|
2016
|
+
const tagStack = [];
|
|
2017
|
+
// Regex to match markdown markers (***, **, *) or spaces or sequences of non-space/non-asterisk characters (words with punctuation)
|
|
2018
|
+
const regex = /(\*\*\*|\*\*|\*|\s+|[^\s\*]+)/g;
|
|
2019
|
+
let match;
|
|
2020
|
+
while ((match = regex.exec(text)) !== null) {
|
|
2021
|
+
const token = match[0];
|
|
2022
|
+
if (token.trim() === '') {
|
|
2023
|
+
// Ignore spaces
|
|
2024
|
+
continue;
|
|
2025
|
+
}
|
|
2026
|
+
if (token === '***') {
|
|
2027
|
+
if (tagStack.length > 0 && tagStack[tagStack.length - 1] === 'bold italic') {
|
|
2028
|
+
tagStack.pop(); // Closing bold italic
|
|
2029
|
+
}
|
|
2030
|
+
else {
|
|
2031
|
+
tagStack.push('bold italic'); // Opening bold italic
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
else if (token === '**') {
|
|
2035
|
+
if (tagStack.length > 0 && tagStack[tagStack.length - 1] === 'bold') {
|
|
2036
|
+
tagStack.pop(); // Closing bold
|
|
2037
|
+
}
|
|
2038
|
+
else {
|
|
2039
|
+
tagStack.push('bold'); // Opening bold
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
else if (token === '*') {
|
|
2043
|
+
if (tagStack.length > 0 && tagStack[tagStack.length - 1] === 'italic') {
|
|
2044
|
+
tagStack.pop(); // Closing italic
|
|
2045
|
+
}
|
|
2046
|
+
else {
|
|
2047
|
+
tagStack.push('italic'); // Opening italic
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
else {
|
|
2051
|
+
// It's a word (including punctuation)
|
|
2052
|
+
const currentTag = tagStack.length > 0 ? tagStack[tagStack.length - 1] : '';
|
|
2053
|
+
result.push({ word: token, tag: currentTag });
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
// Note: This implementation assumes correctly matched opening and closing tags.
|
|
2057
|
+
// Unclosed tags at the end of the string will be ignored.
|
|
2058
|
+
return result;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1718
2061
|
class TextHighlighterComponent {
|
|
1719
2062
|
constructor() {
|
|
2063
|
+
// Inputs
|
|
1720
2064
|
this.message = input.required(); // Or input.required<MessageAudio>() if always expected
|
|
1721
|
-
|
|
2065
|
+
// Outputs
|
|
2066
|
+
this.playAudio = output();
|
|
2067
|
+
this.audioCompleted = output();
|
|
2068
|
+
this.wordClicked = output(); // Output for clicked word with messageId
|
|
2069
|
+
// Signal States
|
|
2070
|
+
this.wordWithMeta = signal([]); // Signal for plain text words when no transcription
|
|
1722
2071
|
this.isPlaying = signal(false); // Signal for play state
|
|
1723
2072
|
// Signals State computed from the input message
|
|
2073
|
+
this.wordsWithMetaState = [];
|
|
1724
2074
|
this.iconState = computed(() => {
|
|
1725
2075
|
if (this.isLoading()) {
|
|
1726
2076
|
return 'isLoading';
|
|
@@ -1742,25 +2092,21 @@ class TextHighlighterComponent {
|
|
|
1742
2092
|
const msg = this.message(); // Read the input signal
|
|
1743
2093
|
return msg?.text || msg?.content || 'N/A';
|
|
1744
2094
|
});
|
|
1745
|
-
this.classTag = computed(() => this.message()?.tag);
|
|
2095
|
+
this.classTag = computed(() => this.message()?.tag); // tag can be at message level or word level this is for message level
|
|
1746
2096
|
// Computed signal for transcription availability
|
|
1747
2097
|
this.hasTranscription = computed(() => {
|
|
1748
2098
|
const hasTranscription = !!this.message()?.transcriptionTimestamps && this.message()?.transcriptionTimestamps.length > 0;
|
|
1749
2099
|
return hasTranscription;
|
|
1750
2100
|
});
|
|
1751
|
-
this.playAudio = output();
|
|
1752
|
-
this.audioCompleted = output();
|
|
1753
2101
|
this.audioElement = null; // Audio element for playback
|
|
1754
2102
|
this.destroy$ = new Subject(); // Cleanup localsubject
|
|
1755
2103
|
// Inject services
|
|
1756
|
-
this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
|
|
1757
2104
|
this.destroyRef = inject(DestroyRef);
|
|
1758
2105
|
// Effect to react to message changes and re-initialize
|
|
1759
2106
|
effect(() => {
|
|
1760
2107
|
const currentMsg = this.message(); // Read the input signal
|
|
1761
|
-
|
|
1762
|
-
if (currentMsg) {
|
|
1763
|
-
// Re-run initialization logic whenever the message signal changes
|
|
2108
|
+
// Somethimes message changes but audio is already playing.
|
|
2109
|
+
if (currentMsg && !this.isPlaying()) {
|
|
1764
2110
|
this.initializeBasedOnMessage(currentMsg);
|
|
1765
2111
|
// Check if shouldPlayAudio flag is set
|
|
1766
2112
|
if (currentMsg.shouldPlayAudio) {
|
|
@@ -1768,21 +2114,15 @@ class TextHighlighterComponent {
|
|
|
1768
2114
|
}
|
|
1769
2115
|
}
|
|
1770
2116
|
else {
|
|
1771
|
-
//
|
|
1772
|
-
this.cleanupAudio();
|
|
1773
|
-
this.highlightedWords.set([]);
|
|
2117
|
+
// this.cleanupAudio();
|
|
1774
2118
|
}
|
|
1775
|
-
// No cdr.markForCheck() needed here usually if template bindings use signals/computed
|
|
1776
2119
|
});
|
|
1777
2120
|
// Keep the effect for highlightedWords for now, might be redundant if template binds directly
|
|
1778
2121
|
effect(() => {
|
|
1779
|
-
|
|
1780
|
-
this.
|
|
2122
|
+
// some how i need to isolate the reading, becouse reading in main effect is very dangerous since later is set and produces infinite loop
|
|
2123
|
+
this.wordsWithMetaState = this.wordWithMeta();
|
|
1781
2124
|
});
|
|
1782
2125
|
}
|
|
1783
|
-
/**
|
|
1784
|
-
* Track function for ngFor to improve performance
|
|
1785
|
-
*/
|
|
1786
2126
|
trackByIndex(index, item) {
|
|
1787
2127
|
return index;
|
|
1788
2128
|
}
|
|
@@ -1805,75 +2145,58 @@ class TextHighlighterComponent {
|
|
|
1805
2145
|
}
|
|
1806
2146
|
// Initialize words and sync based on transcription presence
|
|
1807
2147
|
if (this.hasTranscription()) {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
2148
|
+
const wordsAndTimestamps = [];
|
|
2149
|
+
// NOTE: This merge is only going to work if transcriptionTimestamps and wordsWithMetaState have the same length
|
|
2150
|
+
// wordsWithMetaState is initialized here in initializePlainTextWords but transcriptionTimestamps in the father, check later if i have bugs.
|
|
2151
|
+
msg?.transcriptionTimestamps?.forEach((word, index) => {
|
|
2152
|
+
wordsAndTimestamps.push({ ...word, ...this.wordsWithMetaState[index], index });
|
|
2153
|
+
});
|
|
2154
|
+
console.log(wordsAndTimestamps);
|
|
2155
|
+
this.subcribeToAudioSync(wordsAndTimestamps); // Pass timestamps
|
|
1812
2156
|
}
|
|
1813
2157
|
else {
|
|
1814
2158
|
this.subscribeToEndAudio();
|
|
2159
|
+
this.initializePlainTextWords(msg.text || msg.content || ''); // Initialize plain text words
|
|
1815
2160
|
}
|
|
1816
2161
|
}
|
|
1817
|
-
/**
|
|
1818
|
-
* Initialize the audio element and set up event listeners
|
|
1819
|
-
*/
|
|
1820
2162
|
initializeAudio(audioUrl) {
|
|
1821
|
-
// Clean up any existing audio element and listeners first
|
|
1822
2163
|
this.cleanupAudio();
|
|
1823
2164
|
this.audioElement = new Audio(audioUrl); // Use passed URL
|
|
1824
2165
|
}
|
|
1825
|
-
/**
|
|
1826
|
-
* Initialize highlighted words from transcription timestamps
|
|
1827
|
-
*/
|
|
1828
|
-
initializeHighlightedWords(timestamps) {
|
|
1829
|
-
const initialWords = timestamps.map((word, index) => ({
|
|
1830
|
-
word: word.word,
|
|
1831
|
-
index,
|
|
1832
|
-
isHighlighted: false,
|
|
1833
|
-
}));
|
|
1834
|
-
this.highlightedWords.set(initialWords);
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
* Set up audio synchronization with text
|
|
1838
|
-
*/
|
|
1839
2166
|
subcribeToAudioSync(timestamps) {
|
|
1840
|
-
// Accept timestamps
|
|
1841
2167
|
if (!this.audioElement) {
|
|
1842
2168
|
// Guard against missing audio element
|
|
1843
2169
|
return;
|
|
1844
2170
|
}
|
|
1845
|
-
// Important: Clean up previous listeners before setting up new ones
|
|
1846
2171
|
this.destroy$.next(); // Signal previous subscriptions to complete
|
|
1847
|
-
|
|
2172
|
+
console.log('Subscribing to audio sync with timestamps:', timestamps);
|
|
1848
2173
|
fromEvent(this.audioElement, 'timeupdate')
|
|
1849
|
-
.pipe(takeUntilDestroyed(this.destroyRef), // Use
|
|
1850
|
-
takeUntil(this.destroy$), // Use manual subject for re-initialization cleanup
|
|
2174
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$), // Use manual subject for re-initialization cleanup
|
|
1851
2175
|
map(() => this.audioElement?.currentTime || 0))
|
|
1852
2176
|
.subscribe((currentTime) => {
|
|
1853
2177
|
// Use the passed timestamps array
|
|
1854
2178
|
const updatedWords = timestamps.map((word, index) => {
|
|
1855
2179
|
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1856
|
-
|
|
2180
|
+
console.log(isHighlighted, currentTime, word.start, word.end, word.start - 0.15 && currentTime < word.end + 0.15);
|
|
2181
|
+
return { word: word.word, index, isHighlighted, tag: word.tag };
|
|
1857
2182
|
});
|
|
1858
|
-
this.
|
|
2183
|
+
this.wordWithMeta.set(updatedWords);
|
|
1859
2184
|
});
|
|
1860
2185
|
// Listen to ended event for cleanup
|
|
1861
2186
|
fromEvent(this.audioElement, 'ended')
|
|
1862
2187
|
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
|
|
1863
2188
|
.subscribe(() => {
|
|
1864
2189
|
// Reset highlighting when audio ends
|
|
1865
|
-
const resetWords = this.
|
|
2190
|
+
const resetWords = this.wordWithMeta().map((word) => ({
|
|
1866
2191
|
// Read current words signal
|
|
1867
2192
|
...word,
|
|
1868
2193
|
isHighlighted: false,
|
|
1869
2194
|
}));
|
|
1870
|
-
this.
|
|
2195
|
+
this.wordWithMeta.set(resetWords);
|
|
1871
2196
|
// Set isPlaying to false when audio finishes
|
|
1872
2197
|
this.isPlaying.set(false);
|
|
1873
|
-
// Emit audio completed event with the current message
|
|
1874
2198
|
const currentMsg = this.message();
|
|
1875
2199
|
if (currentMsg) {
|
|
1876
|
-
// Reset the shouldPlayAudio flag
|
|
1877
2200
|
currentMsg.shouldPlayAudio = false;
|
|
1878
2201
|
this.audioCompleted.emit(currentMsg);
|
|
1879
2202
|
}
|
|
@@ -1906,11 +2229,11 @@ class TextHighlighterComponent {
|
|
|
1906
2229
|
this.audioElement = null;
|
|
1907
2230
|
this.destroy$.next(); // Ensure listeners tied to this audio instance are cleaned up
|
|
1908
2231
|
// Reset highlighting immediately on cleanup
|
|
1909
|
-
const resetWords = this.
|
|
2232
|
+
const resetWords = this.wordWithMeta().map((word) => ({
|
|
1910
2233
|
...word,
|
|
1911
2234
|
isHighlighted: false,
|
|
1912
2235
|
}));
|
|
1913
|
-
this.
|
|
2236
|
+
this.wordWithMeta.set(resetWords);
|
|
1914
2237
|
}
|
|
1915
2238
|
}
|
|
1916
2239
|
/**
|
|
@@ -1933,7 +2256,6 @@ class TextHighlighterComponent {
|
|
|
1933
2256
|
// For simplicity, let's re-call initializeAudio here if needed,
|
|
1934
2257
|
// though ideally the effect handles it.
|
|
1935
2258
|
this.initializeAudio(currentMsg.audioUrl); // Ensure it's created if somehow missed
|
|
1936
|
-
// this.initializeBasedOnMessage(currentMsg);
|
|
1937
2259
|
this.startAudioPlayback();
|
|
1938
2260
|
}
|
|
1939
2261
|
else if (currentMsg) {
|
|
@@ -1942,23 +2264,44 @@ class TextHighlighterComponent {
|
|
|
1942
2264
|
}
|
|
1943
2265
|
}
|
|
1944
2266
|
/**
|
|
1945
|
-
*
|
|
2267
|
+
* Initialize plain text words by splitting the message text
|
|
1946
2268
|
*/
|
|
2269
|
+
initializePlainTextWords(text) {
|
|
2270
|
+
const words = extractTags(text);
|
|
2271
|
+
const plainWords = words.map((word, index) => ({
|
|
2272
|
+
word: word.word,
|
|
2273
|
+
index: index,
|
|
2274
|
+
isHighlighted: false,
|
|
2275
|
+
tag: word.tag,
|
|
2276
|
+
}));
|
|
2277
|
+
this.wordWithMeta.set(plainWords);
|
|
2278
|
+
}
|
|
2279
|
+
onWordClick(wordData) {
|
|
2280
|
+
const currentMsg = this.message();
|
|
2281
|
+
if (!currentMsg) {
|
|
2282
|
+
console.warn('Cannot emit wordClicked: message input is not available.');
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
// Determine the correct data structure based on input type
|
|
2286
|
+
const clickedWord = 'isHighlighted' in wordData
|
|
2287
|
+
? { word: wordData.word, index: wordData.index, messageId: currentMsg.messageId }
|
|
2288
|
+
: { ...wordData, messageId: currentMsg.messageId };
|
|
2289
|
+
this.wordClicked.emit(clickedWord);
|
|
2290
|
+
}
|
|
1947
2291
|
startAudioPlayback() {
|
|
1948
2292
|
if (!this.audioElement)
|
|
1949
2293
|
return;
|
|
1950
|
-
// Play the audio
|
|
1951
2294
|
this.audioElement.play().catch((error) => {
|
|
1952
2295
|
console.error('Error playing audio:', error);
|
|
1953
2296
|
});
|
|
1954
2297
|
this.isPlaying.set(true); // Set play state to true
|
|
1955
2298
|
}
|
|
1956
2299
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1957
|
-
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 <
|
|
2300
|
+
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", wordClicked: "wordClicked" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\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 @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span [class]=\"word.tag\" [class.highlight]=\"word.isHighlighted\" (click)=\"onWordClick(word)\" [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }} </span\n >\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}.italic{font-style:italic;color:#6495ed}.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 }); }
|
|
1958
2301
|
}
|
|
1959
2302
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, decorators: [{
|
|
1960
2303
|
type: Component,
|
|
1961
|
-
args: [{ selector: 'dc-text-highlighter', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <
|
|
2304
|
+
args: [{ selector: 'dc-text-highlighter', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\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 @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span [class]=\"word.tag\" [class.highlight]=\"word.isHighlighted\" (click)=\"onWordClick(word)\" [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }} </span\n >\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}.italic{font-style:italic;color:#6495ed}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"] }]
|
|
1962
2305
|
}], ctorParameters: () => [] });
|
|
1963
2306
|
|
|
1964
2307
|
class MessageOrchestratorComponent {
|
|
@@ -1967,7 +2310,6 @@ class MessageOrchestratorComponent {
|
|
|
1967
2310
|
this.messages = input.required();
|
|
1968
2311
|
this.messageRole = input.required();
|
|
1969
2312
|
this.messagesSignal = signal([]);
|
|
1970
|
-
// Effect to update messagesSignal when input messages change
|
|
1971
2313
|
this.messagesEffect = effect(() => {
|
|
1972
2314
|
// Get the latest messages from the input
|
|
1973
2315
|
const currentMessages = this.messages();
|
|
@@ -2104,12 +2446,16 @@ class MessageOrchestratorComponent {
|
|
|
2104
2446
|
this.isGenerating = false;
|
|
2105
2447
|
}
|
|
2106
2448
|
}
|
|
2449
|
+
onWordClicked(wordData) {
|
|
2450
|
+
console.log('Word clicked in MessageOrchestrator:', wordData);
|
|
2451
|
+
this.conversationService.notifyWordClicked(wordData);
|
|
2452
|
+
}
|
|
2107
2453
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2108
|
-
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
|
|
2454
|
+
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: "<div class=\"message-orchestrator-container\">\n @for (message of messagesSignal(); track message.messageId) {\n <dc-text-highlighter\n [message]=\"message\"\n (playAudio)=\"playAudio.emit($event)\"\n (audioCompleted)=\"onAudioCompleted($event)\"\n (wordClicked)=\"onWordClicked($event)\" />\n }\n</div>\n", styles: [":host{display:block}.word-options-popup{position:absolute;border:1px solid #ccc;background:#fff;padding:5px;z-index:1000}\n"], dependencies: [{ kind: "component", type: TextHighlighterComponent, selector: "dc-text-highlighter", inputs: ["message"], outputs: ["playAudio", "audioCompleted", "wordClicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2109
2455
|
}
|
|
2110
2456
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, decorators: [{
|
|
2111
2457
|
type: Component,
|
|
2112
|
-
args: [{ selector: 'dc-message-orchestrator', imports: [TextHighlighterComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter
|
|
2458
|
+
args: [{ selector: 'dc-message-orchestrator', imports: [TextHighlighterComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"message-orchestrator-container\">\n @for (message of messagesSignal(); track message.messageId) {\n <dc-text-highlighter\n [message]=\"message\"\n (playAudio)=\"playAudio.emit($event)\"\n (audioCompleted)=\"onAudioCompleted($event)\"\n (wordClicked)=\"onWordClicked($event)\" />\n }\n</div>\n", styles: [":host{display:block}.word-options-popup{position:absolute;border:1px solid #ccc;background:#fff;padding:5px;z-index:1000}\n"] }]
|
|
2113
2459
|
}] });
|
|
2114
2460
|
|
|
2115
2461
|
const EVALUATION_EMOJIS = {
|
|
@@ -2131,7 +2477,7 @@ class ChatMessageComponent {
|
|
|
2131
2477
|
this.messageTranslation = computed(() => this.chatMessage()?.translation);
|
|
2132
2478
|
this.isUserMessage = computed(() => this.chatMessage()?.role === ChatRole.User);
|
|
2133
2479
|
this.evaluationEmoji = computed(() => {
|
|
2134
|
-
const score = this.chatMessage()?.evaluation?.score;
|
|
2480
|
+
const score = this.chatMessage()?.evaluation?.['score'];
|
|
2135
2481
|
if (score === null || score === undefined) {
|
|
2136
2482
|
return '';
|
|
2137
2483
|
}
|
|
@@ -2159,14 +2505,17 @@ class ChatMessageComponent {
|
|
|
2159
2505
|
}
|
|
2160
2506
|
showEvaluation() {
|
|
2161
2507
|
console.log('showEvaluation', this.chatMessage().evaluation);
|
|
2162
|
-
alert(this.chatMessage().evaluation?.feedback || 'No feedback available');
|
|
2508
|
+
alert(this.chatMessage().evaluation?.['feedback'] || 'No feedback available');
|
|
2509
|
+
}
|
|
2510
|
+
printData() {
|
|
2511
|
+
console.log(this.chatMessage());
|
|
2163
2512
|
}
|
|
2164
2513
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2165
|
-
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]=\"chatMessage().imgUrl\" alt=\"AI\" 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 (
|
|
2514
|
+
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\" (click)=\"printData()\">\n <img [src]=\"chatMessage().imgUrl\" alt=\"AI\" 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 (chatMessage().evaluation?.['translation']) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage().evaluation?.['translation'] }}\n </div>\n } @if (isUserMessage() && chatMessage().evaluation) {\n <div class=\"translation\" style=\"color: white\">\n <hr class=\"divider\" />\n {{ chatMessage().evaluation?.['feedback'] }}\n </div>\n\n <div style=\"position: absolute; bottom: -10px; left: 20px\">\n <span>{{ evaluationEmoji() }}</span>\n <span class=\"pointer\" (click)=\"showEvaluation()\"> \uD83E\uDDD0 </span>\n </div>\n\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\" (click)=\"printData()\">\n <div class=\"avatar user-avatar\">\n <img [src]=\"chatMessage()?.imgUrl || '/assets/defaults/avatar_user.jpg'\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <br />\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;position:relative}.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 }); }
|
|
2166
2515
|
}
|
|
2167
2516
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
2168
2517
|
type: Component,
|
|
2169
|
-
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]=\"chatMessage().imgUrl\" alt=\"AI\" 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 (
|
|
2518
|
+
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\" (click)=\"printData()\">\n <img [src]=\"chatMessage().imgUrl\" alt=\"AI\" 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 (chatMessage().evaluation?.['translation']) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage().evaluation?.['translation'] }}\n </div>\n } @if (isUserMessage() && chatMessage().evaluation) {\n <div class=\"translation\" style=\"color: white\">\n <hr class=\"divider\" />\n {{ chatMessage().evaluation?.['feedback'] }}\n </div>\n\n <div style=\"position: absolute; bottom: -10px; left: 20px\">\n <span>{{ evaluationEmoji() }}</span>\n <span class=\"pointer\" (click)=\"showEvaluation()\"> \uD83E\uDDD0 </span>\n </div>\n\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\" (click)=\"printData()\">\n <div class=\"avatar user-avatar\">\n <img [src]=\"chatMessage()?.imgUrl || '/assets/defaults/avatar_user.jpg'\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <br />\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;position:relative}.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"] }]
|
|
2170
2519
|
}] });
|
|
2171
2520
|
|
|
2172
2521
|
class ChatMessagesListComponent {
|
|
@@ -2178,7 +2527,7 @@ class ChatMessagesListComponent {
|
|
|
2178
2527
|
this.conversationService = inject(ConversationService);
|
|
2179
2528
|
this.elementRef = inject(ElementRef);
|
|
2180
2529
|
// State
|
|
2181
|
-
this.aiIcon = 'assets/
|
|
2530
|
+
this.aiIcon = 'assets/defaults/avatar_ai.webp';
|
|
2182
2531
|
this.isThinking = this.conversationService.isThinking();
|
|
2183
2532
|
this.messages = computed(() => {
|
|
2184
2533
|
// Get the actual array of messages from the signal by calling it as a function
|
|
@@ -2211,11 +2560,11 @@ class ChatMessagesListComponent {
|
|
|
2211
2560
|
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
2212
2561
|
}
|
|
2213
2562
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2214
|
-
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 message.messageId) {\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;height:100%}.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 }); }
|
|
2563
|
+
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 message.messageId) {\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;height:100%}.thinking-container{padding:.5rem 0;border-radius:20px;background:#fff}.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 }); }
|
|
2215
2564
|
}
|
|
2216
2565
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
2217
2566
|
type: Component,
|
|
2218
|
-
args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [ChatMessageComponent, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"messages-container\">\n @for (message of messages(); track message.messageId) {\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;height:100%}.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"] }]
|
|
2567
|
+
args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [ChatMessageComponent, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"messages-container\">\n @for (message of messages(); track message.messageId) {\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;height:100%}.thinking-container{padding:.5rem 0;border-radius:20px;background:#fff}.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"] }]
|
|
2219
2568
|
}], ctorParameters: () => [] });
|
|
2220
2569
|
|
|
2221
2570
|
const SpeedDescription = {
|
|
@@ -2411,12 +2760,14 @@ class DCChatComponent {
|
|
|
2411
2760
|
// Inputs
|
|
2412
2761
|
this.chatUserSettings = this.userDataExchange.getUserChatSettings(); // Default to user data exchange
|
|
2413
2762
|
// Signal Inputs
|
|
2414
|
-
|
|
2415
|
-
this.
|
|
2763
|
+
// readonly evaluatorAgentCard = input<IMiniAgentCard>(); // @Deprecated in favor of appAgentTask
|
|
2764
|
+
this.taskOnUserMessage = input();
|
|
2765
|
+
this.taskOnAssistantMessage = input();
|
|
2416
2766
|
this.parseDict = input({});
|
|
2417
2767
|
// Outputs
|
|
2418
|
-
this.sendMessage = output(); //
|
|
2768
|
+
this.sendMessage = output(); // Notifies about various events happening inside the chat
|
|
2419
2769
|
this.goalCompleted = output(); // notifies when user completes goal (score reaches 100)
|
|
2770
|
+
// readonly wordClicked = output<WordData>(); // Replaced by sendMessage with ChatEventType.WordClicked
|
|
2420
2771
|
// Signals States
|
|
2421
2772
|
this.messages = signal([]);
|
|
2422
2773
|
// States
|
|
@@ -2430,9 +2781,18 @@ class DCChatComponent {
|
|
|
2430
2781
|
this.goalCompleted.emit();
|
|
2431
2782
|
}
|
|
2432
2783
|
});
|
|
2784
|
+
// Effect to watch for word clicks from the service
|
|
2785
|
+
effect(() => {
|
|
2786
|
+
const clickedWordData = this.conversationService.getWordClickedSignal()();
|
|
2787
|
+
if (clickedWordData) {
|
|
2788
|
+
// Emit through the consolidated sendMessage output
|
|
2789
|
+
this.sendMessage.emit({ type: ChatEventType.WordClicked, payload: clickedWordData });
|
|
2790
|
+
// Optional: Reset the signal in the service if you only want to emit once per click
|
|
2791
|
+
// this.conversationService.notifyWordClicked(null);
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2433
2794
|
}
|
|
2434
2795
|
async ngOnInit() {
|
|
2435
|
-
console.log(this.parseDict());
|
|
2436
2796
|
if (this.conversationSettings) {
|
|
2437
2797
|
this.conversationService.setDestroyed(false);
|
|
2438
2798
|
await this.conversationService.initConversationWithSettings(this.conversationSettings);
|
|
@@ -2440,10 +2800,13 @@ class DCChatComponent {
|
|
|
2440
2800
|
else {
|
|
2441
2801
|
await this.conversationService.initConversationWithAgentCard(this.agentCard, this.parseDict());
|
|
2442
2802
|
}
|
|
2803
|
+
this.evaluationService.resetScore();
|
|
2443
2804
|
}
|
|
2444
2805
|
ngOnDestroy() {
|
|
2445
2806
|
// Mark conversation as destroyed to prevent async operations
|
|
2446
2807
|
this.conversationService.setDestroyed(true);
|
|
2808
|
+
// Ensure the microphone is stopped when the chat component is destroyed
|
|
2809
|
+
this.chatFooterComponent?.stopMic();
|
|
2447
2810
|
}
|
|
2448
2811
|
/**
|
|
2449
2812
|
* Open chat settings dialog
|
|
@@ -2477,12 +2840,15 @@ class DCChatComponent {
|
|
|
2477
2840
|
await this.ngOnInit();
|
|
2478
2841
|
}
|
|
2479
2842
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2480
|
-
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 }, conversationSettings: { classPropertyName: "conversationSettings", publicName: "conversationSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null },
|
|
2843
|
+
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 }, conversationSettings: { classPropertyName: "conversationSettings", publicName: "conversationSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, taskOnUserMessage: { classPropertyName: "taskOnUserMessage", publicName: "taskOnUserMessage", isSignal: true, isRequired: false, transformFunction: null }, taskOnAssistantMessage: { classPropertyName: "taskOnAssistantMessage", publicName: "taskOnAssistantMessage", isSignal: true, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", goalCompleted: "goalCompleted" }, providers: [DialogService], viewQueries: [{ propertyName: "chatFooterComponent", first: true, predicate: ChatFooterComponent, descendants: true }], 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\" [taskOnUserMessage]=\"taskOnUserMessage()\" [taskOnAssistantMessage]=\"taskOnAssistantMessage()\"> </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", "taskOnUserMessage", "taskOnAssistantMessage", "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 }); }
|
|
2481
2844
|
}
|
|
2482
2845
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
2483
2846
|
type: Component,
|
|
2484
|
-
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\" [
|
|
2485
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
2847
|
+
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\" [taskOnUserMessage]=\"taskOnUserMessage()\" [taskOnAssistantMessage]=\"taskOnAssistantMessage()\"> </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"] }]
|
|
2848
|
+
}], ctorParameters: () => [], propDecorators: { chatFooterComponent: [{
|
|
2849
|
+
type: ViewChild,
|
|
2850
|
+
args: [ChatFooterComponent]
|
|
2851
|
+
}], chatUserSettings: [{
|
|
2486
2852
|
type: Input
|
|
2487
2853
|
}], conversationSettings: [{
|
|
2488
2854
|
type: Input
|
|
@@ -2837,6 +3203,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2837
3203
|
type: Input
|
|
2838
3204
|
}] } });
|
|
2839
3205
|
|
|
3206
|
+
// import { extractJsonFromResponse } from '@dataclouder/ngx-agent-cards';
|
|
2840
3207
|
class DCAgentCardFormComponent {
|
|
2841
3208
|
getSettings() {
|
|
2842
3209
|
const imageSettings = {
|
|
@@ -3219,7 +3586,7 @@ class DCAgentCardFormComponent {
|
|
|
3219
3586
|
textEngine: TextEngines.SimpleText,
|
|
3220
3587
|
conversationType: ConversationType.General,
|
|
3221
3588
|
});
|
|
3222
|
-
const jsonData =
|
|
3589
|
+
const jsonData = extractJsonFromResponse(response.content);
|
|
3223
3590
|
if (jsonData) {
|
|
3224
3591
|
this.toastService.success({ title: 'Character generated', subtitle: 'No te olvides de guardar cambios si estas seguro' });
|
|
3225
3592
|
this.form.controls.characterCard.patchValue({ data: jsonData });
|
|
@@ -3232,18 +3599,6 @@ class DCAgentCardFormComponent {
|
|
|
3232
3599
|
}
|
|
3233
3600
|
this.isGenerating = false;
|
|
3234
3601
|
}
|
|
3235
|
-
extractJsonFromResponse(content) {
|
|
3236
|
-
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
3237
|
-
if (!jsonMatch)
|
|
3238
|
-
return null;
|
|
3239
|
-
try {
|
|
3240
|
-
return JSON.parse(jsonMatch[0]);
|
|
3241
|
-
}
|
|
3242
|
-
catch (error) {
|
|
3243
|
-
console.error('Error parsing JSON:', error);
|
|
3244
|
-
return null;
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
3602
|
async removeSticker(sticker) {
|
|
3248
3603
|
console.log('remove sticker', sticker);
|
|
3249
3604
|
this.storageService.deleteImage(sticker.path);
|
|
@@ -3278,7 +3633,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
3278
3633
|
DynamicDialogModule,
|
|
3279
3634
|
PopoverModule,
|
|
3280
3635
|
ProviderSelectorComponent,
|
|
3281
|
-
AccountPlatformForm
|
|
3636
|
+
AccountPlatformForm,
|
|
3282
3637
|
], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">✖</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
|
|
3283
3638
|
}], ctorParameters: () => [] });
|
|
3284
3639
|
|
|
@@ -3308,11 +3663,11 @@ class DCConversationCardUIComponent {
|
|
|
3308
3663
|
this.onCardAction.emit({ event: 'delete', card: this.card() });
|
|
3309
3664
|
}
|
|
3310
3665
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3311
|
-
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 <
|
|
3666
|
+
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 <p class=\"text-xl font-bold text-shadow-lg/30\">{{ card().title }}</p>\n\n <!-- <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5> -->\n\n <p style=\"margin-top: 40px\">\n <span class=\"title text-shadow-lg/30\" [innerHTML]=\"card().characterCard?.data.creator_notes | truncate : 200\"></span>\n </p>\n\n @if(card().taken){\n\n <div style=\"position: absolute; bottom: 10px; left: 10px\">\n <p-tag icon=\"pi pi-check-circle\" severity=\"secondary\" value=\"Tomada\" [rounded]=\"true\" />\n </div>\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:1rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column;justify-content:space-between}.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"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4$2.Tag, selector: "p-tag", inputs: ["style", "styleClass", "severity", "value", "icon", "rounded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3312
3667
|
}
|
|
3313
3668
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
|
|
3314
3669
|
type: Component,
|
|
3315
|
-
args: [{ selector: 'dc-agent-card-default-ui', imports: [PopoverModule, TruncatePipe, ButtonModule, SpeedDialModule, CardModule], changeDetection: ChangeDetectionStrategy.OnPush, 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 <
|
|
3670
|
+
args: [{ selector: 'dc-agent-card-default-ui', imports: [PopoverModule, TruncatePipe, ButtonModule, SpeedDialModule, CardModule, TagModule], changeDetection: ChangeDetectionStrategy.OnPush, 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 <p class=\"text-xl font-bold text-shadow-lg/30\">{{ card().title }}</p>\n\n <!-- <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5> -->\n\n <p style=\"margin-top: 40px\">\n <span class=\"title text-shadow-lg/30\" [innerHTML]=\"card().characterCard?.data.creator_notes | truncate : 200\"></span>\n </p>\n\n @if(card().taken){\n\n <div style=\"position: absolute; bottom: 10px; left: 10px\">\n <p-tag icon=\"pi pi-check-circle\" severity=\"secondary\" value=\"Tomada\" [rounded]=\"true\" />\n </div>\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:1rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column;justify-content:space-between}.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"] }]
|
|
3316
3671
|
}] });
|
|
3317
3672
|
|
|
3318
3673
|
// This component contains a really avanced strategy to dinamically render Conversation Cards Details so every app can implement it with their own Style and Behavior
|
|
@@ -3328,9 +3683,11 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3328
3683
|
const route = inject(ActivatedRoute);
|
|
3329
3684
|
const router = inject(Router);
|
|
3330
3685
|
super(route, router);
|
|
3686
|
+
// Services
|
|
3331
3687
|
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
3332
3688
|
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
3333
3689
|
this.cdr = inject(ChangeDetectorRef);
|
|
3690
|
+
// Inputs
|
|
3334
3691
|
this.viewMode = 'cards';
|
|
3335
3692
|
this.customCardComponent = input(undefined);
|
|
3336
3693
|
this.showOptions = input(true);
|
|
@@ -3343,7 +3700,15 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3343
3700
|
ngOnInit() {
|
|
3344
3701
|
this.columns = DefaultColumns;
|
|
3345
3702
|
// this.buttonActions = DefaultActions;
|
|
3346
|
-
this.filterConfig.returnProps = {
|
|
3703
|
+
this.filterConfig.returnProps = {
|
|
3704
|
+
_id: 1,
|
|
3705
|
+
title: 1,
|
|
3706
|
+
assets: 1,
|
|
3707
|
+
description: 1,
|
|
3708
|
+
'characterCard.data.description': 1,
|
|
3709
|
+
'characterCard.data.name': 1,
|
|
3710
|
+
'characterCard.data.creator_notes': 1,
|
|
3711
|
+
};
|
|
3347
3712
|
this.loadConversationCards();
|
|
3348
3713
|
this.cardComponent = this.customCardComponent() || DCConversationCardUIComponent;
|
|
3349
3714
|
}
|
|
@@ -3458,7 +3823,7 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3458
3823
|
}
|
|
3459
3824
|
}
|
|
3460
3825
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3461
|
-
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]=\"
|
|
3826
|
+
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]=\"showOptions()\" (onFilterAction)=\"doFilterBarAction($event)\" (onNew)=\"newAgentCard()\"></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", "items", "customFilters"], outputs: ["onFilterAction", "onChangeSort", "onNew"] }, { 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: ["columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
3462
3827
|
}
|
|
3463
3828
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, decorators: [{
|
|
3464
3829
|
type: Component,
|
|
@@ -3472,7 +3837,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
3472
3837
|
SpeedDialModule,
|
|
3473
3838
|
QuickTableComponent,
|
|
3474
3839
|
CommonModule,
|
|
3475
|
-
], standalone: true, template: "<dc-filter-bar [isAdmin]=\"
|
|
3840
|
+
], standalone: true, template: "<dc-filter-bar [isAdmin]=\"showOptions()\" (onFilterAction)=\"doFilterBarAction($event)\" (onNew)=\"newAgentCard()\"></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"] }]
|
|
3476
3841
|
}], ctorParameters: () => [], propDecorators: { viewMode: [{
|
|
3477
3842
|
type: Input
|
|
3478
3843
|
}], outlets: [{
|
|
@@ -3538,11 +3903,11 @@ class DcAgentCardDetailsComponent {
|
|
|
3538
3903
|
this.cdr.markForCheck();
|
|
3539
3904
|
}
|
|
3540
3905
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3541
|
-
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", 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" }] }); }
|
|
3906
|
+
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", 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}::ng-deep .info-button .p-button{background-color:#ffffff47}\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" }] }); }
|
|
3542
3907
|
}
|
|
3543
3908
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
|
|
3544
3909
|
type: Component,
|
|
3545
|
-
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [ButtonModule, CardModule, ParseCardPipe], 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", 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"] }]
|
|
3910
|
+
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [ButtonModule, CardModule, ParseCardPipe], 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", 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}::ng-deep .info-button .p-button{background-color:#ffffff47}\n"] }]
|
|
3546
3911
|
}], propDecorators: { agentCardId: [{
|
|
3547
3912
|
type: Input
|
|
3548
3913
|
}] } });
|
|
@@ -3556,5 +3921,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
3556
3921
|
* Generated bundle index. Do not edit.
|
|
3557
3922
|
*/
|
|
3558
3923
|
|
|
3559
|
-
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, EAccountsPlatform, EvalResultStringDefinition, 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 };
|
|
3924
|
+
export { AgentCardListComponent, AgentCardProgressStatus, AgentCardsAbstractService, AgentUserProgressService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatEventType, ChatMessage, ChatRole, ChatUserSettings, ContextType, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, EAccountsPlatform, EvalResultStringDefinition, LangCodeDescriptionEs, MessageAudio, MessageOrchestratorComponent, PopupService, 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 };
|
|
3560
3925
|
//# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map
|