@dataclouder/ngx-agent-cards 0.0.84 → 0.0.86
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 +1515 -578
- package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
- package/lib/components/audio-text-sync/audio-text-sync.component.d.ts +42 -0
- package/lib/components/chat/chat.models.d.ts +2 -0
- package/lib/components/chat/dc-chat.component.d.ts +5 -7
- package/lib/components/chat-container/chat-container.component.d.ts +66 -0
- package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +30 -0
- package/lib/components/chat-container/chat-header/chat-header.component.d.ts +20 -0
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +30 -0
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +3 -0
- package/lib/components/chat-container/chat-messages-list/chat-message/message-content/message-content.component.d.ts +24 -0
- package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +22 -0
- package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +13 -0
- package/lib/components/dc-agent-card-details/dc-agent-card-details.component.d.ts +8 -4
- package/lib/components/dc-agent-card-lists/agent-card-default-ui/agent-card-default-ui.component.d.ts +2 -1
- package/lib/components/dc-agent-card-lists/dc-agent-card-lists.component.d.ts +3 -5
- package/lib/components/dc-agent-form/dc-agent-card-form.component.d.ts +12 -8
- package/lib/components/icons/icon-map.d.ts +0 -3
- package/lib/models/agent.models.d.ts +11 -15
- package/lib/models/conversation-enums.d.ts +2 -0
- package/lib/pipes/parse-card.pipe.d.ts +10 -0
- package/lib/services/audio-text-sync.service.d.ts +57 -0
- package/lib/services/conversation.service.d.ts +31 -0
- package/lib/services/dc-conversation-builder.service.d.ts +6 -1
- package/lib/services/evaluation.service.d.ts +17 -0
- package/lib/services/message-processing.service.d.ts +11 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -1
- package/lib/components/chat-message/chat-message.component.d.ts +0 -28
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, Inject, Component, Input,
|
|
3
|
-
import * as
|
|
2
|
+
import { InjectionToken, Injectable, Inject, EventEmitter, Component, Input, Output, inject, DestroyRef, signal, Injector, effect, runInInjectionContext, ChangeDetectionStrategy, Pipe, ViewChild, Optional, ViewChildren } from '@angular/core';
|
|
3
|
+
import * as i1$2 from '@angular/common';
|
|
4
4
|
import { CommonModule, NgComponentOutlet } from '@angular/common';
|
|
5
|
-
import * as i1 from '
|
|
6
|
-
import * as i1$1 from '@angular/forms';
|
|
7
|
-
import { ReactiveFormsModule, FormControl, FormsModule } from '@angular/forms';
|
|
8
|
-
import * as i1$2 from 'primeng/dynamicdialog';
|
|
5
|
+
import * as i1$3 from 'primeng/dynamicdialog';
|
|
9
6
|
import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog';
|
|
10
|
-
import * as i6 from 'primeng/
|
|
11
|
-
import { SkeletonModule } from 'primeng/skeleton';
|
|
12
|
-
import * as i6$1 from 'primeng/dialog';
|
|
7
|
+
import * as i6 from 'primeng/dialog';
|
|
13
8
|
import { DialogModule } from 'primeng/dialog';
|
|
14
|
-
import * as
|
|
15
|
-
import { TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
16
|
-
import { DCMicComponent } from '@dataclouder/ngx-mic';
|
|
17
|
-
import * as i7$2 from 'primeng/progressbar';
|
|
9
|
+
import * as i2 from 'primeng/progressbar';
|
|
18
10
|
import { ProgressBarModule } from 'primeng/progressbar';
|
|
19
|
-
import * as
|
|
11
|
+
import * as i1 from '@angular/forms';
|
|
12
|
+
import { FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
13
|
+
import { DCMicComponent } from '@dataclouder/ngx-mic';
|
|
14
|
+
import * as i3 from 'primeng/textarea';
|
|
15
|
+
import { TextareaModule } from 'primeng/textarea';
|
|
16
|
+
import * as i7 from 'primeng/button';
|
|
17
|
+
import { ButtonModule } from 'primeng/button';
|
|
18
|
+
import * as i1$1 from '@angular/platform-browser';
|
|
19
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
20
|
+
import { BehaviorSubject, Subject, fromEvent, filter } from 'rxjs';
|
|
21
|
+
import { takeUntil, map } from 'rxjs/operators';
|
|
22
|
+
import * as i4 from 'primeng/skeleton';
|
|
23
|
+
import { SkeletonModule } from 'primeng/skeleton';
|
|
24
|
+
import * as i4$2 from 'primeng/checkbox';
|
|
20
25
|
import { CheckboxModule } from 'primeng/checkbox';
|
|
21
26
|
import { SliderModule } from 'primeng/slider';
|
|
22
|
-
import * as i3 from 'primeng/radiobutton';
|
|
27
|
+
import * as i3$1 from 'primeng/radiobutton';
|
|
23
28
|
import { RadioButtonModule } from 'primeng/radiobutton';
|
|
24
|
-
import * as i7$1 from 'primeng/button';
|
|
25
|
-
import { ButtonModule } from 'primeng/button';
|
|
26
29
|
import * as i8 from 'primeng/rating';
|
|
27
30
|
import { RatingModule } from 'primeng/rating';
|
|
28
31
|
import * as i5 from 'primeng/table';
|
|
29
32
|
import { TableModule } from 'primeng/table';
|
|
30
33
|
import { BadgeModule } from 'primeng/badge';
|
|
31
|
-
import * as i7 from 'primeng/tooltip';
|
|
34
|
+
import * as i7$1 from 'primeng/tooltip';
|
|
32
35
|
import { TooltipModule } from 'primeng/tooltip';
|
|
33
|
-
import
|
|
34
|
-
import * as
|
|
36
|
+
import * as i4$1 from 'primeng/api';
|
|
37
|
+
import * as i6$1 from '@dataclouder/ngx-core';
|
|
38
|
+
import { TOAST_ALERTS_TOKEN, AudioSpeed as AudioSpeed$1, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
35
39
|
import { OverlayModule } from '@angular/cdk/overlay';
|
|
36
40
|
import { PortalModule } from '@angular/cdk/portal';
|
|
37
|
-
import * as i8$1 from 'primeng/textarea';
|
|
38
|
-
import { TextareaModule } from 'primeng/textarea';
|
|
39
41
|
import * as i5$1 from 'primeng/inputtext';
|
|
40
42
|
import { InputTextModule } from 'primeng/inputtext';
|
|
41
43
|
import * as i11 from 'primeng/togglebutton';
|
|
@@ -50,13 +52,13 @@ import * as i14 from 'primeng/select';
|
|
|
50
52
|
import { SelectModule } from 'primeng/select';
|
|
51
53
|
import * as i15 from 'primeng/popover';
|
|
52
54
|
import { PopoverModule } from 'primeng/popover';
|
|
53
|
-
import * as i3$
|
|
55
|
+
import * as i3$2 from 'primeng/card';
|
|
54
56
|
import { CardModule } from 'primeng/card';
|
|
55
|
-
import * as i4$
|
|
57
|
+
import * as i4$3 from 'primeng/dropdown';
|
|
56
58
|
import { DropdownModule } from 'primeng/dropdown';
|
|
57
59
|
import { ChipModule } from 'primeng/chip';
|
|
58
|
-
import * as i1$
|
|
59
|
-
import * as i3$
|
|
60
|
+
import * as i1$4 from '@angular/router';
|
|
61
|
+
import * as i3$3 from 'primeng/paginator';
|
|
60
62
|
import { PaginatorModule } from 'primeng/paginator';
|
|
61
63
|
import * as i2$3 from 'primeng/speeddial';
|
|
62
64
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
@@ -79,8 +81,13 @@ var EAccountsPlatform;
|
|
|
79
81
|
EAccountsPlatform["Youtube"] = "youtube";
|
|
80
82
|
})(EAccountsPlatform || (EAccountsPlatform = {}));
|
|
81
83
|
class WordTimestamps {
|
|
84
|
+
constructor() {
|
|
85
|
+
this.highlighted = false; // Initialize with false and use proper boolean type
|
|
86
|
+
}
|
|
82
87
|
}
|
|
83
|
-
class
|
|
88
|
+
class MessageAudio {
|
|
89
|
+
}
|
|
90
|
+
class ChatMultiMessage extends MessageAudio {
|
|
84
91
|
}
|
|
85
92
|
var ChatRole;
|
|
86
93
|
(function (ChatRole) {
|
|
@@ -89,7 +96,7 @@ var ChatRole;
|
|
|
89
96
|
ChatRole["Assistant"] = "assistant";
|
|
90
97
|
ChatRole["AssistantHelper"] = "assistantHelper";
|
|
91
98
|
})(ChatRole || (ChatRole = {}));
|
|
92
|
-
class ChatMessage {
|
|
99
|
+
class ChatMessage extends MessageAudio {
|
|
93
100
|
}
|
|
94
101
|
class ChatUserSettings {
|
|
95
102
|
}
|
|
@@ -119,6 +126,20 @@ const LangCodeDescriptionEs = {
|
|
|
119
126
|
pt: 'Portugués',
|
|
120
127
|
fr: 'Frances',
|
|
121
128
|
};
|
|
129
|
+
// TODO: use the default settings when the user is not set
|
|
130
|
+
const defaultconvUserSettings = {
|
|
131
|
+
realTime: false,
|
|
132
|
+
repeatRecording: false,
|
|
133
|
+
fixGrammar: false,
|
|
134
|
+
superHearing: false,
|
|
135
|
+
voice: '',
|
|
136
|
+
autoTranslate: false,
|
|
137
|
+
highlightWords: true,
|
|
138
|
+
synthVoice: true,
|
|
139
|
+
model: { modelName: null, provider: 'google' },
|
|
140
|
+
speed: null,
|
|
141
|
+
speedRate: null, // temporal
|
|
142
|
+
};
|
|
122
143
|
class VoiceTTSOption {
|
|
123
144
|
}
|
|
124
145
|
const VoiceTTSOptions = [
|
|
@@ -531,17 +552,25 @@ class DCConversationPromptBuilderService {
|
|
|
531
552
|
this.parseConversation(initialConversation, parseDict);
|
|
532
553
|
return initialConversation;
|
|
533
554
|
}
|
|
534
|
-
getDefaultParseDict(parseDict,
|
|
555
|
+
getDefaultParseDict(parseDict, card) {
|
|
556
|
+
// Example of return { char: 'AI Bot', user: 'jordan', ... custom params, base: 'spanish' };
|
|
557
|
+
// conversation will parse values with curren value in parseDcit:
|
|
558
|
+
// Hello {{user}} im {{char}} your are talking {{base}} -> hello jordan im AI bot your are talking spanish
|
|
559
|
+
// important you have to implement userDataExchange to return what you want in parseDict that have sense for your app.
|
|
535
560
|
if (parseDict) {
|
|
536
561
|
return parseDict;
|
|
537
562
|
}
|
|
538
|
-
|
|
563
|
+
else {
|
|
539
564
|
parseDict = this.userDataExchange.getParseDict();
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
565
|
+
const cardParseDict = this.getDefaultCardParseDict(card);
|
|
566
|
+
parseDict = { ...parseDict, ...cardParseDict };
|
|
567
|
+
return parseDict;
|
|
543
568
|
}
|
|
544
|
-
|
|
569
|
+
}
|
|
570
|
+
getDefaultCardParseDict(card) {
|
|
571
|
+
// bot so far i don't use more than the character name.
|
|
572
|
+
// this is what silly tavern call macros https://docs.sillytavern.app/usage/core-concepts/macros/#general-macros
|
|
573
|
+
return { char: card.characterCard.data.name || 'Bot' };
|
|
545
574
|
}
|
|
546
575
|
convertConversationToHtml(messages, jailBrake = '') {
|
|
547
576
|
let finalPrompt = '';
|
|
@@ -643,8 +672,8 @@ class DCConversationPromptBuilderService {
|
|
|
643
672
|
last_prompt: jailBrakePrompt,
|
|
644
673
|
textEngine: agent.conversationSettings.textEngine,
|
|
645
674
|
conversationType: ConversationType.Scenario,
|
|
646
|
-
voice: agent.tts.voice,
|
|
647
|
-
secondaryVoice: agent.tts.secondaryVoice,
|
|
675
|
+
voice: agent.conversationSettings.tts.voice,
|
|
676
|
+
secondaryVoice: agent.conversationSettings.tts.secondaryVoice,
|
|
648
677
|
// overrideConversationSettings: { autoTranslate: false, highlightWords: false, realTime: false, provider: 'google' },
|
|
649
678
|
};
|
|
650
679
|
return settings;
|
|
@@ -662,127 +691,119 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
662
691
|
args: [USER_DATA_EXCHANGE]
|
|
663
692
|
}] }] });
|
|
664
693
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
stroke-width="2"
|
|
720
|
-
>
|
|
721
|
-
<!-- Brain outline -->
|
|
722
|
-
<path
|
|
723
|
-
d="M50 10
|
|
724
|
-
C60 10, 70 20, 70 30
|
|
725
|
-
S60 50, 50 50
|
|
726
|
-
S30 40, 30 30
|
|
727
|
-
S40 10, 50 10Z"
|
|
728
|
-
fill="#B3E5FC"
|
|
729
|
-
/>
|
|
730
|
-
|
|
731
|
-
<!-- Circuit lines -->
|
|
732
|
-
<line x1="50" y1="50" x2="50" y2="70" stroke="#0288D1" stroke-width="2" />
|
|
733
|
-
<line x1="50" y1="70" x2="40" y2="80" stroke="#0288D1" stroke-width="2" />
|
|
734
|
-
<line x1="50" y1="70" x2="60" y2="80" stroke="#0288D1" stroke-width="2" />
|
|
735
|
-
|
|
736
|
-
<!-- Nodes -->
|
|
737
|
-
<circle cx="50" cy="70" r="2" fill="#0288D1" />
|
|
738
|
-
<circle cx="40" cy="80" r="3" fill="#0288D1" />
|
|
739
|
-
<circle cx="60" cy="80" r="3" fill="#0288D1" />
|
|
740
|
-
</svg>
|
|
741
|
-
`,
|
|
742
|
-
};
|
|
694
|
+
class ChatHeaderComponent {
|
|
695
|
+
constructor(agentCardService) {
|
|
696
|
+
this.agentCardService = agentCardService;
|
|
697
|
+
this.isAdmin = false;
|
|
698
|
+
this.alternativeConversation = [];
|
|
699
|
+
this.restartConversationEvent = new EventEmitter();
|
|
700
|
+
this.showInfoEvent = new EventEmitter();
|
|
701
|
+
this.settingsClickEvent = new EventEmitter();
|
|
702
|
+
}
|
|
703
|
+
restartConversation(conversation = null) {
|
|
704
|
+
this.restartConversationEvent.emit(conversation);
|
|
705
|
+
}
|
|
706
|
+
showInfo() {
|
|
707
|
+
this.showInfoEvent.emit();
|
|
708
|
+
}
|
|
709
|
+
settingsClick() {
|
|
710
|
+
this.settingsClickEvent.emit();
|
|
711
|
+
}
|
|
712
|
+
async changeConversationCard() {
|
|
713
|
+
const response = prompt('¿Qué conversación quieres usar? Escribe el titulo ejemplo: word_reflection_level_1_base_es');
|
|
714
|
+
if (response) {
|
|
715
|
+
const filters = {
|
|
716
|
+
filters: {
|
|
717
|
+
title: { $regex: response },
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
console.log('filters', filters);
|
|
721
|
+
const conversationCards = await this.agentCardService.filterConversationCards(filters);
|
|
722
|
+
console.log('conversationCards', conversationCards);
|
|
723
|
+
this.alternativeConversation = conversationCards.rows;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatHeaderComponent, deps: [{ token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
727
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatHeaderComponent, isStandalone: true, selector: "dc-chat-header", inputs: { isAdmin: "isAdmin", alternativeConversation: "alternativeConversation", agentCard: "agentCard" }, outputs: { restartConversationEvent: "restartConversationEvent", showInfoEvent: "showInfoEvent", settingsClickEvent: "settingsClickEvent" }, ngImport: i0, template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
|
|
728
|
+
}
|
|
729
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatHeaderComponent, decorators: [{
|
|
730
|
+
type: Component,
|
|
731
|
+
args: [{ selector: 'dc-chat-header', standalone: true, imports: [CommonModule], template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"] }]
|
|
732
|
+
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
733
|
+
type: Inject,
|
|
734
|
+
args: [CONVERSATION_AI_TOKEN]
|
|
735
|
+
}] }], propDecorators: { isAdmin: [{
|
|
736
|
+
type: Input
|
|
737
|
+
}], alternativeConversation: [{
|
|
738
|
+
type: Input
|
|
739
|
+
}], agentCard: [{
|
|
740
|
+
type: Input
|
|
741
|
+
}], restartConversationEvent: [{
|
|
742
|
+
type: Output
|
|
743
|
+
}], showInfoEvent: [{
|
|
744
|
+
type: Output
|
|
745
|
+
}], settingsClickEvent: [{
|
|
746
|
+
type: Output
|
|
747
|
+
}] } });
|
|
743
748
|
|
|
744
|
-
class
|
|
745
|
-
constructor(
|
|
746
|
-
this.
|
|
747
|
-
this.
|
|
748
|
-
this.
|
|
749
|
+
class ChatFooterComponent {
|
|
750
|
+
constructor() {
|
|
751
|
+
this.isAIThinking = false;
|
|
752
|
+
this.score = 0;
|
|
753
|
+
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
754
|
+
this.sendMessage = new EventEmitter();
|
|
755
|
+
this.textInputChanged = new EventEmitter();
|
|
756
|
+
this.micFinishedEvent = new EventEmitter();
|
|
757
|
+
this.chatInputControl = new FormControl();
|
|
749
758
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
759
|
+
/**
|
|
760
|
+
* Sets the input text in the textarea
|
|
761
|
+
* @param text The text to set
|
|
762
|
+
*/
|
|
763
|
+
setInputText(text) {
|
|
764
|
+
this.chatInputControl.setValue(text);
|
|
765
|
+
this.textInputChanged.emit(text);
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Handles the mic finished event
|
|
769
|
+
* @param eventBlob The blob event from the mic component
|
|
770
|
+
*/
|
|
771
|
+
micFinished(eventBlob) {
|
|
772
|
+
this.micFinishedEvent.emit(eventBlob);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Sends the user message
|
|
776
|
+
*/
|
|
777
|
+
sendUserMessage() {
|
|
778
|
+
if (this.isAIThinking || !this.chatInputControl.value) {
|
|
779
|
+
return;
|
|
754
780
|
}
|
|
781
|
+
const text = this.chatInputControl.value;
|
|
782
|
+
const message = {
|
|
783
|
+
content: text,
|
|
784
|
+
role: ChatRole.User,
|
|
785
|
+
};
|
|
786
|
+
this.sendMessage.emit(message);
|
|
787
|
+
this.chatInputControl.setValue('');
|
|
755
788
|
}
|
|
756
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type:
|
|
757
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type:
|
|
758
|
-
<span
|
|
759
|
-
[innerHTML]="sanitizedIcon"
|
|
760
|
-
[style.display]="'inline-flex'"
|
|
761
|
-
[style.alignItems]="'center'"
|
|
762
|
-
[style.justifyContent]="'center'"
|
|
763
|
-
[style.width.px]="size"
|
|
764
|
-
[style.height.px]="size"
|
|
765
|
-
[style.color]="color"></span>
|
|
766
|
-
`, isInline: 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"] }); }
|
|
789
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
790
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: "isAIThinking", score: "score", micSettings: "micSettings" }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged", micFinishedEvent: "micFinishedEvent" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.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"] }] }); }
|
|
767
791
|
}
|
|
768
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type:
|
|
792
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
769
793
|
type: Component,
|
|
770
|
-
args: [{ selector: 'dc-
|
|
771
|
-
|
|
772
|
-
[innerHTML]="sanitizedIcon"
|
|
773
|
-
[style.display]="'inline-flex'"
|
|
774
|
-
[style.alignItems]="'center'"
|
|
775
|
-
[style.justifyContent]="'center'"
|
|
776
|
-
[style.width.px]="size"
|
|
777
|
-
[style.height.px]="size"
|
|
778
|
-
[style.color]="color"></span>
|
|
779
|
-
`, 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"] }]
|
|
780
|
-
}], ctorParameters: () => [{ type: i1.DomSanitizer }], propDecorators: { name: [{
|
|
794
|
+
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [CommonModule, ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
|
|
795
|
+
}], propDecorators: { isAIThinking: [{
|
|
781
796
|
type: Input
|
|
782
|
-
}],
|
|
797
|
+
}], score: [{
|
|
783
798
|
type: Input
|
|
784
|
-
}],
|
|
799
|
+
}], micSettings: [{
|
|
785
800
|
type: Input
|
|
801
|
+
}], sendMessage: [{
|
|
802
|
+
type: Output
|
|
803
|
+
}], textInputChanged: [{
|
|
804
|
+
type: Output
|
|
805
|
+
}], micFinishedEvent: [{
|
|
806
|
+
type: Output
|
|
786
807
|
}] } });
|
|
787
808
|
|
|
788
809
|
function markdownToHtml(markdown) {
|
|
@@ -986,104 +1007,507 @@ function removeAllEmojis(text) {
|
|
|
986
1007
|
return text.replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}\u{1F3FB}-\u{1F3FF}\u{E0020}-\u{E007F}\u{FE00}-\u{FE0F}\u{1F900}-\u{1F9FF}\u{1F1E6}-\u{1F1FF}]/gu, '');
|
|
987
1008
|
}
|
|
988
1009
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
args: [{
|
|
1027
|
-
name: 'startDividerToHtml',
|
|
1028
|
-
standalone: true,
|
|
1029
|
-
}]
|
|
1030
|
-
}] });
|
|
1031
|
-
class MdToHtmlArrayPipe {
|
|
1032
|
-
// devuelve un array de objetos con el contenido y el tag
|
|
1033
|
-
transform(text) {
|
|
1034
|
-
const htmlArray = convertToHTML(text);
|
|
1035
|
-
return htmlArray.map((val) => val.content).join('');
|
|
1036
|
-
}
|
|
1037
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1038
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, isStandalone: true, name: "mdToHtmlArray" }); }
|
|
1039
|
-
}
|
|
1040
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, decorators: [{
|
|
1041
|
-
type: Pipe,
|
|
1042
|
-
args: [{
|
|
1043
|
-
name: 'mdToHtmlArray',
|
|
1044
|
-
standalone: true,
|
|
1045
|
-
}]
|
|
1046
|
-
}] });
|
|
1010
|
+
const ICONS = {
|
|
1011
|
+
chat: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
1012
|
+
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
|
|
1013
|
+
</svg>`,
|
|
1014
|
+
play: `<svg
|
|
1015
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1016
|
+
width="16"
|
|
1017
|
+
height="16"
|
|
1018
|
+
viewBox="0 0 24 24"
|
|
1019
|
+
fill="none"
|
|
1020
|
+
stroke="#263042"
|
|
1021
|
+
stroke-width="2"
|
|
1022
|
+
stroke-linecap="round"
|
|
1023
|
+
stroke-linejoin="round">
|
|
1024
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
1025
|
+
<polygon points="10 8 16 12 10 16 10 8"></polygon>
|
|
1026
|
+
</svg>`,
|
|
1027
|
+
loading: `<svg
|
|
1028
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1029
|
+
width="16"
|
|
1030
|
+
height="16"
|
|
1031
|
+
viewBox="0 0 24 24"
|
|
1032
|
+
fill="none"
|
|
1033
|
+
stroke="#263042"
|
|
1034
|
+
stroke-width="2"
|
|
1035
|
+
stroke-linecap="round"
|
|
1036
|
+
stroke-linejoin="round">
|
|
1037
|
+
<line x1="12" y1="2" x2="12" y2="6"></line>
|
|
1038
|
+
<line x1="12" y1="18" x2="12" y2="22"></line>
|
|
1039
|
+
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
|
|
1040
|
+
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
|
|
1041
|
+
<line x1="2" y1="12" x2="6" y2="12"></line>
|
|
1042
|
+
<line x1="18" y1="12" x2="22" y2="12"></line>
|
|
1043
|
+
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
|
|
1044
|
+
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
|
|
1045
|
+
</svg>`,
|
|
1046
|
+
};
|
|
1047
1047
|
|
|
1048
|
-
class
|
|
1049
|
-
constructor(
|
|
1050
|
-
this.
|
|
1051
|
-
this.
|
|
1052
|
-
this.
|
|
1053
|
-
this.isDestroyed = false;
|
|
1054
|
-
// TODO: use the default settings when the user is not set
|
|
1055
|
-
this.defaultconvUserSettings = {
|
|
1056
|
-
realTime: false,
|
|
1057
|
-
repeatRecording: false,
|
|
1058
|
-
fixGrammar: false,
|
|
1059
|
-
superHearing: false,
|
|
1060
|
-
voice: '',
|
|
1061
|
-
autoTranslate: false,
|
|
1062
|
-
highlightWords: true,
|
|
1063
|
-
synthVoice: true,
|
|
1064
|
-
model: { modelName: null, provider: 'google' },
|
|
1065
|
-
speed: null,
|
|
1066
|
-
speedRate: null, // temporal
|
|
1067
|
-
};
|
|
1048
|
+
class IconsComponent {
|
|
1049
|
+
constructor(sanitizer) {
|
|
1050
|
+
this.sanitizer = sanitizer;
|
|
1051
|
+
this.size = 14;
|
|
1052
|
+
this.color = 'currentColor';
|
|
1068
1053
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
return;
|
|
1054
|
+
ngOnChanges(changes) {
|
|
1055
|
+
if (changes['name']) {
|
|
1056
|
+
const svg = ICONS[this.name] || '';
|
|
1057
|
+
this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
|
|
1074
1058
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1059
|
+
}
|
|
1060
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1061
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: IconsComponent, isStandalone: true, selector: "dc-icon", inputs: { name: "name", size: "size", color: "color" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1062
|
+
<span
|
|
1063
|
+
[innerHTML]="sanitizedIcon"
|
|
1064
|
+
[style.display]="'inline-flex'"
|
|
1065
|
+
[style.alignItems]="'center'"
|
|
1066
|
+
[style.justifyContent]="'center'"
|
|
1067
|
+
[style.width.px]="size"
|
|
1068
|
+
[style.height.px]="size"
|
|
1069
|
+
[style.color]="color"></span>
|
|
1070
|
+
`, isInline: 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"] }); }
|
|
1071
|
+
}
|
|
1072
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, decorators: [{
|
|
1073
|
+
type: Component,
|
|
1074
|
+
args: [{ selector: 'dc-icon', template: `
|
|
1075
|
+
<span
|
|
1076
|
+
[innerHTML]="sanitizedIcon"
|
|
1077
|
+
[style.display]="'inline-flex'"
|
|
1078
|
+
[style.alignItems]="'center'"
|
|
1079
|
+
[style.justifyContent]="'center'"
|
|
1080
|
+
[style.width.px]="size"
|
|
1081
|
+
[style.height.px]="size"
|
|
1082
|
+
[style.color]="color"></span>
|
|
1083
|
+
`, 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"] }]
|
|
1084
|
+
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { name: [{
|
|
1085
|
+
type: Input
|
|
1086
|
+
}], size: [{
|
|
1087
|
+
type: Input
|
|
1088
|
+
}], color: [{
|
|
1089
|
+
type: Input
|
|
1090
|
+
}] } });
|
|
1091
|
+
|
|
1092
|
+
class AudioTextSyncService {
|
|
1093
|
+
constructor() {
|
|
1094
|
+
// Maps to store message-specific signals and observables
|
|
1095
|
+
this.highlightedWordsSignalMap = new Map();
|
|
1096
|
+
this.highlightedWords$Map = new Map();
|
|
1097
|
+
// Maps for cleanup and active audio elements
|
|
1098
|
+
this.cleanup$Map = new Map();
|
|
1099
|
+
this.activeAudioMap = new Map();
|
|
1100
|
+
this.destroyRef = inject(DestroyRef);
|
|
1101
|
+
// Ensure cleanup when service is destroyed
|
|
1102
|
+
this.destroyRef.onDestroy(() => {
|
|
1103
|
+
this.stopAllSyncs();
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
/**
|
|
1107
|
+
* Synchronizes audio playback with text transcription
|
|
1108
|
+
* @param audioElement The audio element to sync with
|
|
1109
|
+
* @param transcriptionTimestamps Array of word timestamps
|
|
1110
|
+
* @param messageId Unique identifier for the message
|
|
1111
|
+
*/
|
|
1112
|
+
syncAudioWithText(audioElement, transcriptionTimestamps, messageId) {
|
|
1113
|
+
// Stop any existing sync for this message
|
|
1114
|
+
this.stopSync(messageId);
|
|
1115
|
+
// Create new signal and subject for this message if they don't exist
|
|
1116
|
+
if (!this.highlightedWordsSignalMap.has(messageId)) {
|
|
1117
|
+
this.highlightedWordsSignalMap.set(messageId, signal([]));
|
|
1118
|
+
}
|
|
1119
|
+
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1120
|
+
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1121
|
+
}
|
|
1122
|
+
// Create cleanup subject
|
|
1123
|
+
const cleanup$ = new Subject();
|
|
1124
|
+
this.cleanup$Map.set(messageId, cleanup$);
|
|
1125
|
+
// Store the active audio element
|
|
1126
|
+
this.activeAudioMap.set(messageId, audioElement);
|
|
1127
|
+
// Get the signal and subject for this message
|
|
1128
|
+
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1129
|
+
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1130
|
+
// Initialize the highlighted words state
|
|
1131
|
+
const initialWords = transcriptionTimestamps.map((word, index) => ({
|
|
1132
|
+
word: word.word,
|
|
1133
|
+
index,
|
|
1134
|
+
isHighlighted: false,
|
|
1135
|
+
}));
|
|
1136
|
+
// Update both signal and observable
|
|
1137
|
+
messageSignal.set(initialWords);
|
|
1138
|
+
messageSubject.next(initialWords);
|
|
1139
|
+
// Listen to timeupdate events
|
|
1140
|
+
fromEvent(audioElement, 'timeupdate')
|
|
1141
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$), map(() => audioElement.currentTime))
|
|
1142
|
+
.subscribe((currentTime) => {
|
|
1143
|
+
const updatedWords = transcriptionTimestamps.map((word, index) => {
|
|
1144
|
+
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1145
|
+
return {
|
|
1146
|
+
word: word.word,
|
|
1147
|
+
index,
|
|
1148
|
+
isHighlighted,
|
|
1149
|
+
};
|
|
1150
|
+
});
|
|
1151
|
+
// Update both signal and observable for this message
|
|
1152
|
+
messageSignal.set(updatedWords);
|
|
1153
|
+
messageSubject.next(updatedWords);
|
|
1154
|
+
});
|
|
1155
|
+
// Listen to ended event for cleanup
|
|
1156
|
+
fromEvent(audioElement, 'ended')
|
|
1157
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$))
|
|
1158
|
+
.subscribe(() => {
|
|
1159
|
+
// Reset highlighting when audio ends
|
|
1160
|
+
const resetWords = initialWords.map((word) => ({
|
|
1161
|
+
...word,
|
|
1162
|
+
isHighlighted: false,
|
|
1163
|
+
}));
|
|
1164
|
+
messageSignal.set(resetWords);
|
|
1165
|
+
messageSubject.next(resetWords);
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Stops the sync for a specific message and cleans up resources
|
|
1170
|
+
* @param messageId The ID of the message to stop syncing
|
|
1171
|
+
*/
|
|
1172
|
+
stopSync(messageId) {
|
|
1173
|
+
if (this.activeAudioMap.has(messageId)) {
|
|
1174
|
+
// Get the cleanup subject for this message
|
|
1175
|
+
const cleanup$ = this.cleanup$Map.get(messageId);
|
|
1176
|
+
if (cleanup$) {
|
|
1177
|
+
cleanup$.next();
|
|
1178
|
+
cleanup$.complete();
|
|
1179
|
+
this.cleanup$Map.delete(messageId);
|
|
1180
|
+
}
|
|
1181
|
+
this.activeAudioMap.delete(messageId);
|
|
1182
|
+
// Reset state for this message
|
|
1183
|
+
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1184
|
+
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1185
|
+
if (messageSignal) {
|
|
1186
|
+
messageSignal.set([]);
|
|
1187
|
+
}
|
|
1188
|
+
if (messageSubject) {
|
|
1189
|
+
messageSubject.next([]);
|
|
1078
1190
|
}
|
|
1079
1191
|
}
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Stops all syncs and cleans up all resources
|
|
1195
|
+
*/
|
|
1196
|
+
stopAllSyncs() {
|
|
1197
|
+
// Get all message IDs and stop each sync
|
|
1198
|
+
for (const messageId of this.activeAudioMap.keys()) {
|
|
1199
|
+
this.stopSync(messageId);
|
|
1200
|
+
}
|
|
1201
|
+
// Clear all maps
|
|
1202
|
+
this.highlightedWordsSignalMap.clear();
|
|
1203
|
+
this.highlightedWords$Map.clear();
|
|
1204
|
+
this.cleanup$Map.clear();
|
|
1205
|
+
this.activeAudioMap.clear();
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Returns the highlighted words signal for a specific message
|
|
1209
|
+
* @param messageId The ID of the message
|
|
1210
|
+
*/
|
|
1211
|
+
getHighlightedWordsSignal(messageId) {
|
|
1212
|
+
// Create a new signal for this message if it doesn't exist
|
|
1213
|
+
if (!this.highlightedWordsSignalMap.has(messageId)) {
|
|
1214
|
+
this.highlightedWordsSignalMap.set(messageId, signal([]));
|
|
1215
|
+
}
|
|
1216
|
+
return this.highlightedWordsSignalMap.get(messageId);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Returns the highlighted words observable for a specific message
|
|
1220
|
+
* @param messageId The ID of the message
|
|
1221
|
+
*/
|
|
1222
|
+
getHighlightedWords$(messageId) {
|
|
1223
|
+
// Create a new subject for this message if it doesn't exist
|
|
1224
|
+
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1225
|
+
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1226
|
+
}
|
|
1227
|
+
return this.highlightedWords$Map.get(messageId).asObservable();
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Checks if a word at a specific index is currently highlighted for a specific message
|
|
1231
|
+
* @param messageId The ID of the message
|
|
1232
|
+
* @param index The index of the word to check
|
|
1233
|
+
*/
|
|
1234
|
+
isWordHighlighted(messageId, index) {
|
|
1235
|
+
const messageSignal = this.getHighlightedWordsSignal(messageId);
|
|
1236
|
+
return messageSignal().some((word) => word.index === index && word.isHighlighted);
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Returns an observable that emits true when a word at a specific index is highlighted for a specific message
|
|
1240
|
+
* @param messageId The ID of the message
|
|
1241
|
+
* @param index The index of the word to observe
|
|
1242
|
+
*/
|
|
1243
|
+
isWordHighlighted$(messageId, index) {
|
|
1244
|
+
return this.getHighlightedWords$(messageId).pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
|
|
1245
|
+
}
|
|
1246
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1247
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, providedIn: 'root' }); }
|
|
1248
|
+
}
|
|
1249
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, decorators: [{
|
|
1250
|
+
type: Injectable,
|
|
1251
|
+
args: [{
|
|
1252
|
+
providedIn: 'root',
|
|
1253
|
+
}]
|
|
1254
|
+
}], ctorParameters: () => [] });
|
|
1255
|
+
|
|
1256
|
+
class AudioTextSyncComponent {
|
|
1257
|
+
constructor(cdr, audioTextSyncService) {
|
|
1258
|
+
this.cdr = cdr;
|
|
1259
|
+
this.audioTextSyncService = audioTextSyncService;
|
|
1260
|
+
this.isLoading = false;
|
|
1261
|
+
this.playAudio = new EventEmitter();
|
|
1262
|
+
// Get the highlighted words signal from the service for this specific message
|
|
1263
|
+
this.highlightedWords = signal([]);
|
|
1264
|
+
// Unique ID for this message instance
|
|
1265
|
+
this.messageId = '';
|
|
1266
|
+
this.injector = inject(Injector);
|
|
1267
|
+
// Setup effect to mark for check when highlighted words change
|
|
1268
|
+
effect(() => {
|
|
1269
|
+
// Just accessing the signal in an effect will re-run the effect when the signal changes
|
|
1270
|
+
this.highlightedWords();
|
|
1271
|
+
// Mark for check to ensure the component updates
|
|
1272
|
+
this.cdr.markForCheck();
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Check if the message has transcription timestamps
|
|
1277
|
+
*/
|
|
1278
|
+
get hasTranscription() {
|
|
1279
|
+
const hasTranscription = !!this.message.transcriptionTimestamps;
|
|
1280
|
+
console.log('hasTranscription check:', {
|
|
1281
|
+
hasTranscription,
|
|
1282
|
+
transcriptionTimestamps: this.message.transcriptionTimestamps,
|
|
1283
|
+
messageId: this.messageId,
|
|
1284
|
+
});
|
|
1285
|
+
return hasTranscription;
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Get the message text content
|
|
1289
|
+
*/
|
|
1290
|
+
get messageText() {
|
|
1291
|
+
return this.message.content || this.message.text;
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Track function for ngFor to improve performance
|
|
1295
|
+
* @param index The current item's index
|
|
1296
|
+
* @param item The current item
|
|
1297
|
+
*/
|
|
1298
|
+
trackByIndex(index, item) {
|
|
1299
|
+
return index;
|
|
1300
|
+
}
|
|
1301
|
+
ngOnInit() {
|
|
1302
|
+
// Generate a unique ID for this message instance
|
|
1303
|
+
this.messageId = this.generateMessageId();
|
|
1304
|
+
// Connect to the service's signal for this message
|
|
1305
|
+
// We need to use a different approach since we can't directly assign a read-only signal
|
|
1306
|
+
const messageSignal = this.audioTextSyncService.getHighlightedWordsSignal(this.messageId);
|
|
1307
|
+
// Create an effect to update our local signal when the service signal changes
|
|
1308
|
+
// Use runInInjectionContext to properly create the effect in ngOnInit
|
|
1309
|
+
runInInjectionContext(this.injector, () => {
|
|
1310
|
+
effect(() => {
|
|
1311
|
+
const words = messageSignal();
|
|
1312
|
+
console.log('Effect updating highlightedWords:', {
|
|
1313
|
+
words,
|
|
1314
|
+
messageId: this.messageId,
|
|
1315
|
+
length: words.length,
|
|
1316
|
+
});
|
|
1317
|
+
this.highlightedWords.set(words);
|
|
1318
|
+
});
|
|
1319
|
+
});
|
|
1320
|
+
// If the message has transcription but no highlighted words are set,
|
|
1321
|
+
// initialize them with the message's transcription
|
|
1322
|
+
if (this.hasTranscription && this.highlightedWords().length === 0) {
|
|
1323
|
+
const transcriptionTimestamps = this.message.transcriptionTimestamps || [];
|
|
1324
|
+
const initialWords = transcriptionTimestamps.map((word, index) => ({
|
|
1325
|
+
word: word.word,
|
|
1326
|
+
index,
|
|
1327
|
+
isHighlighted: word.highlighted || false,
|
|
1328
|
+
}));
|
|
1329
|
+
this.highlightedWords.set(initialWords);
|
|
1330
|
+
// Also initialize the service signal for this message
|
|
1331
|
+
// This ensures the words are available even before playing
|
|
1332
|
+
this.audioTextSyncService.syncAudioWithText(this.message['audioHtml'] || new Audio(), this.message.transcriptionTimestamps || [], this.messageId);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Generate a unique ID for this message instance
|
|
1337
|
+
* Uses message content/text and a timestamp to ensure uniqueness
|
|
1338
|
+
*/
|
|
1339
|
+
generateMessageId() {
|
|
1340
|
+
const messageContent = this.message.content || this.message.text || '';
|
|
1341
|
+
const timestamp = new Date().getTime();
|
|
1342
|
+
return `msg_${messageContent.substring(0, 20).replace(/\s+/g, '_')}_${timestamp}`;
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Emit event to play the audio message
|
|
1346
|
+
*/
|
|
1347
|
+
onPlayMessage() {
|
|
1348
|
+
this.playAudio.emit(this.message);
|
|
1349
|
+
}
|
|
1350
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: AudioTextSyncService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1351
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AudioTextSyncComponent, isStandalone: true, selector: "dc-audio-text-sync", inputs: { message: "message", isLoading: "isLoading" }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n\n <!-- Display transcription with highlighting -->\n <div>\n @if (hasTranscription) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n\n } }@else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText\"></span>\n }\n </div>\n</div>\n", styles: [".highlight{background-color:#ff0;border-radius:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1352
|
+
}
|
|
1353
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncComponent, decorators: [{
|
|
1354
|
+
type: Component,
|
|
1355
|
+
args: [{ selector: 'dc-audio-text-sync', standalone: true, imports: [CommonModule, IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n\n <!-- Display transcription with highlighting -->\n <div>\n @if (hasTranscription) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n\n } }@else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText\"></span>\n }\n </div>\n</div>\n", styles: [".highlight{background-color:#ff0;border-radius:3px}\n"] }]
|
|
1356
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: AudioTextSyncService }], propDecorators: { message: [{
|
|
1357
|
+
type: Input
|
|
1358
|
+
}], isLoading: [{
|
|
1359
|
+
type: Input
|
|
1360
|
+
}], playAudio: [{
|
|
1361
|
+
type: Output
|
|
1362
|
+
}] } });
|
|
1363
|
+
|
|
1364
|
+
class MessageContentComponent {
|
|
1365
|
+
constructor() {
|
|
1366
|
+
this.isLoading = false;
|
|
1367
|
+
this.playAudio = new EventEmitter();
|
|
1368
|
+
}
|
|
1369
|
+
get hasTranscription() {
|
|
1370
|
+
return !!this.message.transcriptionTimestamps;
|
|
1371
|
+
}
|
|
1372
|
+
get messageText() {
|
|
1373
|
+
return this.message.content || this.message.text;
|
|
1374
|
+
}
|
|
1375
|
+
get messageTag() {
|
|
1376
|
+
return this.message.tag || null;
|
|
1377
|
+
}
|
|
1378
|
+
onPlayMessage() {
|
|
1379
|
+
this.playAudio.emit(this.message);
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Track function for ngFor to improve performance
|
|
1383
|
+
* @param index The current item's index
|
|
1384
|
+
* @param item The current item
|
|
1385
|
+
*/
|
|
1386
|
+
trackByIndex(index, item) {
|
|
1387
|
+
return index;
|
|
1388
|
+
}
|
|
1389
|
+
ngOnInit() {
|
|
1390
|
+
console.log(this.message);
|
|
1391
|
+
}
|
|
1392
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1393
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: MessageContentComponent, isStandalone: true, selector: "dc-message-content", inputs: { message: "message", isLoading: "isLoading" }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "<!-- Message with transcription -->\n@if (hasTranscription) {\n<dc-audio-text-sync [message]=\"message\" [isLoading]=\"isLoading\" (playAudio)=\"playAudio.emit($event)\"></dc-audio-text-sync>\n}@else {\n<!-- Message without transcription -->\n<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n <span style=\"margin-left: 2px\" [ngClass]=\"messageTag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n}\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: AudioTextSyncComponent, selector: "dc-audio-text-sync", inputs: ["message", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1394
|
+
}
|
|
1395
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageContentComponent, decorators: [{
|
|
1396
|
+
type: Component,
|
|
1397
|
+
args: [{ selector: 'dc-message-content', standalone: true, imports: [CommonModule, IconsComponent, AudioTextSyncComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Message with transcription -->\n@if (hasTranscription) {\n<dc-audio-text-sync [message]=\"message\" [isLoading]=\"isLoading\" (playAudio)=\"playAudio.emit($event)\"></dc-audio-text-sync>\n}@else {\n<!-- Message without transcription -->\n<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n <span style=\"margin-left: 2px\" [ngClass]=\"messageTag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n}\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
1398
|
+
}], ctorParameters: () => [], propDecorators: { message: [{
|
|
1399
|
+
type: Input
|
|
1400
|
+
}], isLoading: [{
|
|
1401
|
+
type: Input
|
|
1402
|
+
}], playAudio: [{
|
|
1403
|
+
type: Output
|
|
1404
|
+
}] } });
|
|
1405
|
+
|
|
1406
|
+
class MultiMessageContentComponent {
|
|
1407
|
+
constructor() {
|
|
1408
|
+
this.isLoading = false;
|
|
1409
|
+
this.playAudio = new EventEmitter();
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Checks if a message has transcription timestamps
|
|
1413
|
+
* @param message The message to check
|
|
1414
|
+
*/
|
|
1415
|
+
hasTranscription(message) {
|
|
1416
|
+
// debugger;
|
|
1417
|
+
return !!message.transcriptionTimestamps;
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Emits the playAudio event with the selected message
|
|
1421
|
+
* @param message The message to play
|
|
1422
|
+
*/
|
|
1423
|
+
onPlayMessage(message) {
|
|
1424
|
+
this.playAudio.emit(message);
|
|
1425
|
+
}
|
|
1426
|
+
ngOnInit() {
|
|
1427
|
+
console.log(this.messages);
|
|
1428
|
+
}
|
|
1429
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiMessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1430
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: MultiMessageContentComponent, isStandalone: true, selector: "dc-multi-message-content", inputs: { messages: "messages", isLoading: "isLoading" }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "@for (message of messages; track message) {\n<!-- Message with transcription -->\n@if (hasTranscription(message)) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <dc-audio-text-sync [message]=\"message\"></dc-audio-text-sync>\n</div>\n}@else {\n<!-- Message without transcription -->\n\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n} }\n\n<!-- Icon template for play/loading status -->\n<ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n</ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: AudioTextSyncComponent, selector: "dc-audio-text-sync", inputs: ["message", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1431
|
+
}
|
|
1432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiMessageContentComponent, decorators: [{
|
|
1433
|
+
type: Component,
|
|
1434
|
+
args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [CommonModule, IconsComponent, AudioTextSyncComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messages; track message) {\n<!-- Message with transcription -->\n@if (hasTranscription(message)) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <dc-audio-text-sync [message]=\"message\"></dc-audio-text-sync>\n</div>\n}@else {\n<!-- Message without transcription -->\n\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n} }\n\n<!-- Icon template for play/loading status -->\n<ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n</ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
1435
|
+
}], ctorParameters: () => [], propDecorators: { messages: [{
|
|
1436
|
+
type: Input
|
|
1437
|
+
}], isLoading: [{
|
|
1438
|
+
type: Input
|
|
1439
|
+
}], playAudio: [{
|
|
1440
|
+
type: Output
|
|
1441
|
+
}] } });
|
|
1442
|
+
|
|
1443
|
+
function matchTranscription(originalText, transcription) {
|
|
1444
|
+
const result = [];
|
|
1445
|
+
const transcriptionMap = new Map();
|
|
1446
|
+
// Create a map of lowercase words to an array of their corresponding objects in transcription
|
|
1447
|
+
for (const obj of transcription) {
|
|
1448
|
+
const lowercaseWord = obj.word.trim().toLowerCase();
|
|
1449
|
+
if (transcriptionMap.has(lowercaseWord)) {
|
|
1450
|
+
transcriptionMap.get(lowercaseWord).push(obj);
|
|
1451
|
+
}
|
|
1080
1452
|
else {
|
|
1081
|
-
|
|
1082
|
-
|
|
1453
|
+
transcriptionMap.set(lowercaseWord, [obj]);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
// Split the original text into an array of words
|
|
1457
|
+
const words = originalText.split(' ');
|
|
1458
|
+
let currentTime = 0;
|
|
1459
|
+
for (const word of words) {
|
|
1460
|
+
const lowercaseWord = word.toLowerCase();
|
|
1461
|
+
const matchedObjs = transcriptionMap.get(lowercaseWord);
|
|
1462
|
+
if (matchedObjs && matchedObjs.length > 0) {
|
|
1463
|
+
const matchedObj = matchedObjs.shift();
|
|
1464
|
+
result.push({
|
|
1465
|
+
word: word,
|
|
1466
|
+
start: Number(matchedObj.start.toFixed(3)),
|
|
1467
|
+
end: Number(matchedObj.end.toFixed(3)),
|
|
1468
|
+
});
|
|
1469
|
+
currentTime = matchedObj.end;
|
|
1470
|
+
}
|
|
1471
|
+
else {
|
|
1472
|
+
result.push({
|
|
1473
|
+
word: word,
|
|
1474
|
+
start: Number(currentTime.toFixed(3)),
|
|
1475
|
+
end: Number(currentTime.toFixed(3)),
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return result;
|
|
1480
|
+
}
|
|
1481
|
+
function extractAudioAndTranscription(message, audio) {
|
|
1482
|
+
message.audioUrl = audio.blobUrl;
|
|
1483
|
+
message.transcription = audio.transcription;
|
|
1484
|
+
if (message.transcription) {
|
|
1485
|
+
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
class ChatMessageComponent {
|
|
1490
|
+
constructor(audioService, agentCardService, audioTextSyncService) {
|
|
1491
|
+
this.audioService = audioService;
|
|
1492
|
+
this.agentCardService = agentCardService;
|
|
1493
|
+
this.audioTextSyncService = audioTextSyncService;
|
|
1494
|
+
this.destroyRef = inject(DestroyRef);
|
|
1495
|
+
this.conversationChatSettings = signal(null);
|
|
1496
|
+
this.isLoading = signal(false);
|
|
1497
|
+
}
|
|
1498
|
+
async ngOnInit() {
|
|
1499
|
+
const settings = await this.agentCardService.getConversationUserChatSettings();
|
|
1500
|
+
this.conversationChatSettings.set(settings);
|
|
1501
|
+
if (this.chatMessage.role === ChatRole.AssistantHelper) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const settings$ = this.conversationChatSettings();
|
|
1505
|
+
if (this.chatMessage.role === ChatRole.User) {
|
|
1506
|
+
if (settings$?.repeatRecording && settings$?.synthVoice) {
|
|
1507
|
+
this.playMessage(this.chatMessage);
|
|
1083
1508
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
// this is an assistant message
|
|
1509
|
+
}
|
|
1510
|
+
else if (settings$?.synthVoice) {
|
|
1087
1511
|
if (this.chatMessage.multiMessages) {
|
|
1088
1512
|
this.generateAndPlayAllAudios(this.chatMessage.multiMessages);
|
|
1089
1513
|
}
|
|
@@ -1092,170 +1516,443 @@ class ChatMessageComponent {
|
|
|
1092
1516
|
}
|
|
1093
1517
|
}
|
|
1094
1518
|
}
|
|
1095
|
-
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
// await this.speechService.speach(message.content, { speed: 1, tone: 0.9 });
|
|
1110
|
-
// }
|
|
1111
|
-
}
|
|
1112
|
-
extractAudioAndTranscription(message, audio) {
|
|
1113
|
-
message.audioUrl = audio.blobUrl;
|
|
1114
|
-
message.transcription = audio.transcription;
|
|
1115
|
-
if (message.transcription) {
|
|
1116
|
-
message.transcriptionTimestamps = this.matchTranscription(message.text || message.content, message.transcription.words);
|
|
1519
|
+
async generateAndPlayAudio(message, overwriteText = null) {
|
|
1520
|
+
this.isLoading.set(true);
|
|
1521
|
+
try {
|
|
1522
|
+
const text = overwriteText || message.content;
|
|
1523
|
+
const ttsObject = this.buildObjectTTSRequest({ ...this.chatMessage, text });
|
|
1524
|
+
if (message.ssml) {
|
|
1525
|
+
ttsObject.ssml = message.ssml;
|
|
1526
|
+
}
|
|
1527
|
+
const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
|
|
1528
|
+
extractAudioAndTranscription(message, speechAudio);
|
|
1529
|
+
this.playMessage(message);
|
|
1530
|
+
}
|
|
1531
|
+
finally {
|
|
1532
|
+
this.isLoading.set(false);
|
|
1117
1533
|
}
|
|
1118
1534
|
}
|
|
1119
1535
|
playMessage(message) {
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
const audioHtml = this.audioService.playAudio(message.audioUrl);
|
|
1123
|
-
message['audioHtml'] = audioHtml;
|
|
1124
|
-
this.subscribeToTimeUpdate(audioHtml, message.transcriptionTimestamps);
|
|
1125
|
-
return audioHtml;
|
|
1126
|
-
}
|
|
1127
|
-
else {
|
|
1128
|
-
// this.speechService.speach(message.content);
|
|
1536
|
+
if (!message.audioUrl) {
|
|
1537
|
+
return null;
|
|
1129
1538
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1539
|
+
const audioElement = this.audioService.playAudio(message.audioUrl);
|
|
1540
|
+
message['audioHtml'] = audioElement;
|
|
1541
|
+
console.log('Playing message with transcription:', {
|
|
1542
|
+
hasTranscription: !!message.transcriptionTimestamps,
|
|
1543
|
+
transcriptionLength: message.transcriptionTimestamps?.length,
|
|
1544
|
+
audioUrl: message.audioUrl,
|
|
1545
|
+
audioElement,
|
|
1546
|
+
});
|
|
1547
|
+
if (message.transcriptionTimestamps) {
|
|
1548
|
+
// Generate a unique ID for this message
|
|
1549
|
+
const messageId = this.generateMessageId(message);
|
|
1550
|
+
console.log('Syncing audio with text:', {
|
|
1551
|
+
messageId,
|
|
1552
|
+
transcriptionTimestamps: message.transcriptionTimestamps,
|
|
1553
|
+
wordCount: message.transcriptionTimestamps.length,
|
|
1554
|
+
});
|
|
1555
|
+
// Use the audio-text sync service instead of direct manipulation
|
|
1556
|
+
// Pass the messageId to ensure each message has its own state
|
|
1557
|
+
this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
|
|
1142
1558
|
}
|
|
1143
|
-
|
|
1144
|
-
audioHtml.ontimeupdate = null;
|
|
1145
|
-
audioHtml = null;
|
|
1146
|
-
// this.chatBroker.currentMessagePlayed$.next(true);
|
|
1147
|
-
};
|
|
1559
|
+
return audioElement;
|
|
1148
1560
|
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Generate a unique ID for a message
|
|
1563
|
+
* Uses message content/text and a timestamp to ensure uniqueness
|
|
1564
|
+
*/
|
|
1565
|
+
generateMessageId(message) {
|
|
1566
|
+
const messageContent = message.content || message.text || '';
|
|
1567
|
+
// Use a safe approach to get an ID-like value since 'id' isn't guaranteed on these types
|
|
1568
|
+
const messageId = message._id || message.id || '';
|
|
1569
|
+
const timestamp = new Date().getTime();
|
|
1570
|
+
return `msg_${messageId}_${messageContent.substring(0, 10).replace(/\s+/g, '_')}_${timestamp}`;
|
|
1571
|
+
}
|
|
1572
|
+
// This method is no longer needed as we're using the AudioTextSyncService
|
|
1573
|
+
// It's kept here as a reference but can be removed
|
|
1574
|
+
/*
|
|
1575
|
+
private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
|
|
1576
|
+
// This functionality has been moved to AudioTextSyncService
|
|
1577
|
+
}
|
|
1578
|
+
*/
|
|
1149
1579
|
generateAndPlayAllAudios(multiMessages) {
|
|
1580
|
+
if (!multiMessages.length)
|
|
1581
|
+
return;
|
|
1150
1582
|
let currentIndex = 0;
|
|
1151
1583
|
const playAudioSequentially = async () => {
|
|
1152
|
-
if (this.isDestroyed)
|
|
1153
|
-
return;
|
|
1154
1584
|
if (currentIndex >= multiMessages.length) {
|
|
1155
|
-
console.log('All audio files have been played.');
|
|
1156
1585
|
return;
|
|
1157
1586
|
}
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if
|
|
1162
|
-
|
|
1587
|
+
const currentMessage = multiMessages[currentIndex];
|
|
1588
|
+
currentMessage.isLoading = true;
|
|
1589
|
+
try {
|
|
1590
|
+
// Process current message audio if needed
|
|
1591
|
+
if (!currentMessage.audioUrl) {
|
|
1592
|
+
if (currentMessage.audioPromise) {
|
|
1593
|
+
await currentMessage.audioPromise;
|
|
1594
|
+
}
|
|
1595
|
+
else {
|
|
1596
|
+
const request = this.buildObjectTTSRequest(currentMessage);
|
|
1597
|
+
currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
1598
|
+
const audio = await currentMessage.audioPromise;
|
|
1599
|
+
extractAudioAndTranscription(currentMessage, audio);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
// Play the current message
|
|
1603
|
+
const audioElement = this.playMessage(currentMessage);
|
|
1604
|
+
if (audioElement) {
|
|
1605
|
+
audioElement.addEventListener('ended', () => {
|
|
1606
|
+
currentIndex++;
|
|
1607
|
+
playAudioSequentially();
|
|
1608
|
+
});
|
|
1163
1609
|
}
|
|
1164
1610
|
else {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
msn.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
1169
|
-
const audio = await msn.audioPromise;
|
|
1170
|
-
this.extractAudioAndTranscription(msn, audio);
|
|
1171
|
-
msn.isLoading = false;
|
|
1172
|
-
this.cdr.detectChanges();
|
|
1611
|
+
// If playback failed, move to next message
|
|
1612
|
+
currentIndex++;
|
|
1613
|
+
playAudioSequentially();
|
|
1173
1614
|
}
|
|
1615
|
+
// Preload next message audio
|
|
1616
|
+
this.preloadNextMessageAudio(multiMessages, currentIndex);
|
|
1174
1617
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1618
|
+
finally {
|
|
1619
|
+
currentMessage.isLoading = false;
|
|
1620
|
+
}
|
|
1621
|
+
};
|
|
1622
|
+
// Start the sequential playback
|
|
1623
|
+
playAudioSequentially();
|
|
1624
|
+
}
|
|
1625
|
+
preloadNextMessageAudio(messages, currentIndex) {
|
|
1626
|
+
if (currentIndex + 1 < messages.length) {
|
|
1627
|
+
const nextMessage = messages[currentIndex + 1];
|
|
1628
|
+
if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
|
|
1629
|
+
const request = this.buildObjectTTSRequest(nextMessage);
|
|
1184
1630
|
nextMessage.isLoading = true;
|
|
1185
1631
|
nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
1186
1632
|
nextMessage.audioPromise.then((audio) => {
|
|
1187
|
-
|
|
1633
|
+
extractAudioAndTranscription(nextMessage, audio);
|
|
1188
1634
|
nextMessage.isLoading = false;
|
|
1189
1635
|
});
|
|
1190
1636
|
}
|
|
1191
|
-
}
|
|
1192
|
-
playAudioSequentially();
|
|
1637
|
+
}
|
|
1193
1638
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const generateTranscription =
|
|
1197
|
-
const speedRate =
|
|
1198
|
-
const speed = typeof
|
|
1199
|
-
const text = removeEmojis(
|
|
1200
|
-
|
|
1201
|
-
text
|
|
1202
|
-
voice:
|
|
1639
|
+
buildObjectTTSRequest(message) {
|
|
1640
|
+
const settings = this.conversationChatSettings();
|
|
1641
|
+
const generateTranscription = settings?.highlightWords ?? false;
|
|
1642
|
+
const speedRate = settings?.speedRate || 0;
|
|
1643
|
+
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed.Regular;
|
|
1644
|
+
const text = removeEmojis(message.text || message.content);
|
|
1645
|
+
return {
|
|
1646
|
+
text,
|
|
1647
|
+
voice: message.voice || this.chatMessage.voice,
|
|
1203
1648
|
generateTranscription,
|
|
1204
1649
|
speedRate,
|
|
1205
1650
|
speed,
|
|
1206
1651
|
};
|
|
1207
|
-
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1652
|
+
}
|
|
1653
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, deps: [{ token: AudioService }, { token: CONVERSATION_AI_TOKEN }, { token: AudioTextSyncService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1654
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: "chatMessage", chatUserSettings: "chatUserSettings" }, ngImport: i0, template: "<!-- Multi-message display -->\n<span class=\"message-container\">\n @if (chatMessage?.multiMessages) {\n <!-- Single message display -->\n <dc-multi-message-content [messages]=\"chatMessage.multiMessages\" (playAudio)=\"playMessage($event)\"> </dc-multi-message-content>\n } @else {\n <dc-message-content [message]=\"chatMessage\" [isLoading]=\"isLoading()\" (playAudio)=\"playMessage($event)\"> </dc-message-content>\n }\n</span>\n\n@if (chatMessage.translation) {\n<!-- Translation display if available -->\n\n<div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n}\n", styles: [":host{display:block}.message-container{display:block;line-height:1.5}::ng-deep .em{color:#0d5878;font-style:italic}::ng-deep .strong{font-weight:700;color:#515151}::ng-deep .em_strong{font-weight:700;font-style:italic;color:#0d5878}.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: "component", type: MessageContentComponent, selector: "dc-message-content", inputs: ["message", "isLoading"], outputs: ["playAudio"] }, { kind: "component", type: MultiMessageContentComponent, selector: "dc-multi-message-content", inputs: ["messages", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1655
|
+
}
|
|
1656
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
1657
|
+
type: Component,
|
|
1658
|
+
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MessageContentComponent, MultiMessageContentComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Multi-message display -->\n<span class=\"message-container\">\n @if (chatMessage?.multiMessages) {\n <!-- Single message display -->\n <dc-multi-message-content [messages]=\"chatMessage.multiMessages\" (playAudio)=\"playMessage($event)\"> </dc-multi-message-content>\n } @else {\n <dc-message-content [message]=\"chatMessage\" [isLoading]=\"isLoading()\" (playAudio)=\"playMessage($event)\"> </dc-message-content>\n }\n</span>\n\n@if (chatMessage.translation) {\n<!-- Translation display if available -->\n\n<div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n}\n", styles: [":host{display:block}.message-container{display:block;line-height:1.5}::ng-deep .em{color:#0d5878;font-style:italic}::ng-deep .strong{font-weight:700;color:#515151}::ng-deep .em_strong{font-weight:700;font-style:italic;color:#0d5878}.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"] }]
|
|
1659
|
+
}], ctorParameters: () => [{ type: AudioService }, { type: AgentCardsAbstractService, decorators: [{
|
|
1660
|
+
type: Inject,
|
|
1661
|
+
args: [CONVERSATION_AI_TOKEN]
|
|
1662
|
+
}] }, { type: AudioTextSyncService }], propDecorators: { chatMessage: [{
|
|
1663
|
+
type: Input
|
|
1664
|
+
}], chatUserSettings: [{
|
|
1665
|
+
type: Input
|
|
1666
|
+
}] } });
|
|
1667
|
+
|
|
1668
|
+
class MessageProcessingService {
|
|
1669
|
+
constructor() { }
|
|
1670
|
+
// Process message for display
|
|
1671
|
+
processMessage(message, conversationSettings, mutate = false) {
|
|
1672
|
+
const defaultVoice = this.getVoice(conversationSettings.voice);
|
|
1673
|
+
let processedMessage;
|
|
1674
|
+
if (mutate) {
|
|
1675
|
+
// Modify the existing message object
|
|
1676
|
+
message.voice = defaultVoice;
|
|
1677
|
+
processedMessage = message;
|
|
1678
|
+
}
|
|
1679
|
+
else {
|
|
1680
|
+
// Create a new message object
|
|
1681
|
+
processedMessage = {
|
|
1682
|
+
content: message.content,
|
|
1683
|
+
role: message.role,
|
|
1684
|
+
voice: defaultVoice
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
// Process based on text engine
|
|
1688
|
+
if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
|
|
1689
|
+
this.processMultiMessages(processedMessage, conversationSettings);
|
|
1690
|
+
}
|
|
1691
|
+
else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
|
|
1692
|
+
if (!conversationSettings.secondaryVoice) {
|
|
1693
|
+
throw new Error('Secondary voice is required for SSML');
|
|
1220
1694
|
}
|
|
1695
|
+
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
1696
|
+
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
1221
1697
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1698
|
+
return processedMessage;
|
|
1699
|
+
}
|
|
1700
|
+
// Process multi-messages from markdown
|
|
1701
|
+
processMultiMessages(message, settings) {
|
|
1702
|
+
// Convert markdown to HTML segments
|
|
1703
|
+
const mdToHtml = convertToHTML(message.content);
|
|
1704
|
+
// Map segments to multi-messages
|
|
1705
|
+
message.multiMessages = mdToHtml.map((val) => {
|
|
1706
|
+
// Get the narrator voice from conversation settings
|
|
1707
|
+
const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
|
|
1708
|
+
// Determine if this is italicized text (narrator)
|
|
1709
|
+
const isItalics = val.tag === 'em';
|
|
1710
|
+
const voice = isItalics ? narratorVoice : message.voice;
|
|
1711
|
+
return {
|
|
1712
|
+
voice,
|
|
1713
|
+
content: val.content,
|
|
1714
|
+
audioUrl: null,
|
|
1715
|
+
audioPromise: null,
|
|
1716
|
+
text: val.text,
|
|
1717
|
+
tag: val.tag
|
|
1718
|
+
};
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
// Replace italics with voice tags for SSML
|
|
1722
|
+
subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
|
|
1723
|
+
const regex = /\*(.*?)\*/g;
|
|
1724
|
+
return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
|
|
1725
|
+
}
|
|
1726
|
+
// Get appropriate voice based on settings
|
|
1727
|
+
getVoice(voice_value, targetLang = 'en') {
|
|
1728
|
+
if ([null, '', 'random'].includes(voice_value)) {
|
|
1729
|
+
const voiceCodes = VoiceTTSOptions.map((val) => val.id);
|
|
1730
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1731
|
+
}
|
|
1732
|
+
if (voice_value === 'randomMan') {
|
|
1733
|
+
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
1734
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1735
|
+
}
|
|
1736
|
+
else if (voice_value === 'randomWoman') {
|
|
1737
|
+
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
1738
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1739
|
+
}
|
|
1740
|
+
else {
|
|
1741
|
+
const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
|
|
1742
|
+
if (voice) {
|
|
1743
|
+
return voice.id;
|
|
1236
1744
|
}
|
|
1237
1745
|
else {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1746
|
+
console.error('Voice not found getting something random', voice_value);
|
|
1747
|
+
return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1752
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, providedIn: 'root' }); }
|
|
1753
|
+
}
|
|
1754
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, decorators: [{
|
|
1755
|
+
type: Injectable,
|
|
1756
|
+
args: [{
|
|
1757
|
+
providedIn: 'root',
|
|
1758
|
+
}]
|
|
1759
|
+
}], ctorParameters: () => [] });
|
|
1760
|
+
|
|
1761
|
+
class ConversationService {
|
|
1762
|
+
constructor(agentCardService, messageProcessingService) {
|
|
1763
|
+
this.agentCardService = agentCardService;
|
|
1764
|
+
this.messageProcessingService = messageProcessingService;
|
|
1765
|
+
this.messagesSignal = signal([]);
|
|
1766
|
+
this.isThinkingSignal = signal(false);
|
|
1767
|
+
this.conversationSettingsSignal = signal(null);
|
|
1768
|
+
this.agentCardSignal = signal(null);
|
|
1769
|
+
this.isDestroyedSignal = signal(false);
|
|
1770
|
+
}
|
|
1771
|
+
// Get messages as a signal
|
|
1772
|
+
getMessages() {
|
|
1773
|
+
return this.messagesSignal;
|
|
1774
|
+
}
|
|
1775
|
+
// Get thinking state as a signal
|
|
1776
|
+
isThinking() {
|
|
1777
|
+
return this.isThinkingSignal;
|
|
1778
|
+
}
|
|
1779
|
+
// Get conversation settings as a signal
|
|
1780
|
+
getConversationSettings() {
|
|
1781
|
+
return this.conversationSettingsSignal;
|
|
1782
|
+
}
|
|
1783
|
+
// Get agent card as a signal
|
|
1784
|
+
getAgentCard() {
|
|
1785
|
+
return this.agentCardSignal;
|
|
1786
|
+
}
|
|
1787
|
+
// Set destroyed state
|
|
1788
|
+
setDestroyed(value) {
|
|
1789
|
+
this.isDestroyedSignal.set(value);
|
|
1790
|
+
}
|
|
1791
|
+
// Initialize conversation
|
|
1792
|
+
async initConversation(agentCard, conversationBuilder, parseDict) {
|
|
1793
|
+
if (!agentCard?.conversationSettings) {
|
|
1794
|
+
throw new Error('Conversation settings are required');
|
|
1795
|
+
}
|
|
1796
|
+
this.agentCardSignal.set(agentCard);
|
|
1797
|
+
// Build conversation settings
|
|
1798
|
+
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1799
|
+
this.conversationSettingsSignal.set(conversationSettings);
|
|
1800
|
+
// Update agent card with conversation settings
|
|
1801
|
+
const updatedAgentCard = { ...agentCard, conversationSettings };
|
|
1802
|
+
this.agentCardSignal.set(updatedAgentCard);
|
|
1803
|
+
if (!conversationSettings.messages) {
|
|
1804
|
+
throw new Error('conversationSettings.messages is required in proper format to start conversation');
|
|
1805
|
+
}
|
|
1806
|
+
// Set initial messages
|
|
1807
|
+
this.messagesSignal.set(conversationSettings.messages);
|
|
1808
|
+
// Find first assistant message
|
|
1809
|
+
const firstAssistantMsg = conversationSettings.messages.find((message) => message.role === ChatRole.Assistant);
|
|
1810
|
+
if (firstAssistantMsg) {
|
|
1811
|
+
// Process the first assistant message
|
|
1812
|
+
this.processAssistantMessage(firstAssistantMsg, true);
|
|
1813
|
+
}
|
|
1814
|
+
else if (agentCard.conversationSettings.autoStart) {
|
|
1815
|
+
// Auto-start conversation if configured
|
|
1816
|
+
await this.sendCurrentConversation();
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
// Send user message
|
|
1820
|
+
async sendUserMessage(message) {
|
|
1821
|
+
if (this.isThinkingSignal()) {
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
// Add message to conversation
|
|
1825
|
+
this.addMessage(message);
|
|
1826
|
+
// Set thinking state
|
|
1827
|
+
this.isThinkingSignal.set(true);
|
|
1828
|
+
try {
|
|
1829
|
+
if (message.audioUrl && this.getAudioPlaybackSetting()) {
|
|
1830
|
+
// Wait for audio to finish playing before sending response
|
|
1831
|
+
await new Promise((resolve) => {
|
|
1832
|
+
if (message.audioHtml) {
|
|
1833
|
+
message.audioHtml.addEventListener('ended', () => {
|
|
1834
|
+
resolve();
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
else {
|
|
1838
|
+
resolve();
|
|
1839
|
+
}
|
|
1242
1840
|
});
|
|
1243
1841
|
}
|
|
1842
|
+
// Send to AI service
|
|
1843
|
+
await this.sendCurrentConversation();
|
|
1244
1844
|
}
|
|
1245
|
-
|
|
1845
|
+
finally {
|
|
1846
|
+
this.isThinkingSignal.set(false);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
// Add message to conversation
|
|
1850
|
+
addMessage(message) {
|
|
1851
|
+
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1246
1852
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1853
|
+
// Process assistant message
|
|
1854
|
+
processAssistantMessage(message, mutate = false) {
|
|
1855
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1856
|
+
if (!conversationSettings) {
|
|
1857
|
+
throw new Error('Conversation settings not initialized');
|
|
1858
|
+
}
|
|
1859
|
+
return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
|
|
1860
|
+
}
|
|
1861
|
+
// Send current conversation to AI
|
|
1862
|
+
async sendCurrentConversation() {
|
|
1863
|
+
if (this.isDestroyedSignal()) {
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
const messages = this.messagesSignal();
|
|
1867
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1868
|
+
const agentCard = this.agentCardSignal();
|
|
1869
|
+
if (!conversationSettings || !agentCard) {
|
|
1870
|
+
throw new Error('Conversation not properly initialized');
|
|
1871
|
+
}
|
|
1872
|
+
if (messages.length > 31) {
|
|
1873
|
+
// Safety limit to prevent infinite conversations
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
let conversationMessages = messages;
|
|
1877
|
+
// Add last prompt if available
|
|
1878
|
+
if (conversationSettings.last_prompt) {
|
|
1879
|
+
conversationMessages = [
|
|
1880
|
+
...messages,
|
|
1881
|
+
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1882
|
+
];
|
|
1883
|
+
}
|
|
1884
|
+
// Prepare conversation DTO
|
|
1885
|
+
const conversation = {
|
|
1886
|
+
messages: conversationMessages,
|
|
1887
|
+
conversationType: conversationSettings.conversationType,
|
|
1888
|
+
textEngine: conversationSettings.textEngine,
|
|
1889
|
+
model: agentCard.model,
|
|
1890
|
+
};
|
|
1891
|
+
// Call AI service
|
|
1892
|
+
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
1893
|
+
if (!response) {
|
|
1894
|
+
console.error('No message returned from AI, is your service working?');
|
|
1895
|
+
throw new Error('No message returned from AI');
|
|
1896
|
+
}
|
|
1897
|
+
// Process response
|
|
1898
|
+
const newMessage = this.processAssistantMessage(response);
|
|
1899
|
+
// Add to messages
|
|
1900
|
+
this.addMessage(newMessage);
|
|
1901
|
+
this.isThinkingSignal.set(false);
|
|
1902
|
+
}
|
|
1903
|
+
// Helper to get audio playback setting
|
|
1904
|
+
getAudioPlaybackSetting() {
|
|
1905
|
+
const agentCard = this.agentCardSignal();
|
|
1906
|
+
return agentCard?.conversationSettings?.repeatRecording || false;
|
|
1907
|
+
}
|
|
1908
|
+
// Reset conversation
|
|
1909
|
+
async resetConversation(agentCard) {
|
|
1910
|
+
if (agentCard) {
|
|
1911
|
+
this.agentCardSignal.set(agentCard);
|
|
1912
|
+
}
|
|
1913
|
+
// Clear messages
|
|
1914
|
+
this.messagesSignal.set([]);
|
|
1915
|
+
// Re-initialize with current agent card
|
|
1916
|
+
const currentAgentCard = agentCard || this.agentCardSignal();
|
|
1917
|
+
if (currentAgentCard) {
|
|
1918
|
+
// Note: This would need the conversationBuilder to be injected or passed
|
|
1919
|
+
// await this.initConversation(currentAgentCard, conversationBuilder);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ConversationService, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: MessageProcessingService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1923
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
|
|
1249
1924
|
}
|
|
1250
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type:
|
|
1251
|
-
type:
|
|
1252
|
-
args: [{
|
|
1253
|
-
|
|
1925
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ConversationService, decorators: [{
|
|
1926
|
+
type: Injectable,
|
|
1927
|
+
args: [{
|
|
1928
|
+
providedIn: 'root',
|
|
1929
|
+
}]
|
|
1930
|
+
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
1254
1931
|
type: Inject,
|
|
1255
1932
|
args: [CONVERSATION_AI_TOKEN]
|
|
1256
|
-
}] }, { type:
|
|
1933
|
+
}] }, { type: MessageProcessingService }] });
|
|
1934
|
+
|
|
1935
|
+
class ChatMessagesListComponent {
|
|
1936
|
+
constructor() {
|
|
1937
|
+
this.aiIcon = 'assets/default/ai.png';
|
|
1938
|
+
this.conversationService = inject(ConversationService);
|
|
1939
|
+
// Get messages and thinking state from the conversation service
|
|
1940
|
+
this.messages = this.conversationService.getMessages();
|
|
1941
|
+
this.isThinking = this.conversationService.isThinking();
|
|
1942
|
+
}
|
|
1943
|
+
// Track messages by their content and role for efficient rendering
|
|
1944
|
+
trackByMessage(index, message) {
|
|
1945
|
+
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
1946
|
+
}
|
|
1947
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1948
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: "chatUserSettings", thinkingImg: "thinkingImg" }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message \n [chatMessage]=\"message\" \n [chatUserSettings]=\"chatUserSettings\">\n </dc-chat-message>\n }\n \n @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\">\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i4.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1949
|
+
}
|
|
1950
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
1951
|
+
type: Component,
|
|
1952
|
+
args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [CommonModule, ChatMessageComponent, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message \n [chatMessage]=\"message\" \n [chatUserSettings]=\"chatUserSettings\">\n </dc-chat-message>\n }\n \n @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\">\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"] }]
|
|
1953
|
+
}], propDecorators: { chatUserSettings: [{
|
|
1257
1954
|
type: Input
|
|
1258
|
-
}],
|
|
1955
|
+
}], thinkingImg: [{
|
|
1259
1956
|
type: Input
|
|
1260
1957
|
}] } });
|
|
1261
1958
|
|
|
@@ -1349,7 +2046,7 @@ class ProviderSelectorComponent {
|
|
|
1349
2046
|
}
|
|
1350
2047
|
}
|
|
1351
2048
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ProviderSelectorComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1352
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ProviderSelectorComponent, isStandalone: true, selector: "dc-provider-selector", inputs: { parentForm: "parentForm" }, ngImport: i0, template: "<div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Providers:</b>\n <div style=\"display: flex; gap: 10px\" [formGroup]=\"parentForm\">\n <div class=\"space\">\n <p-radioButton value=\"groq\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Groq</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openai\" formControlName=\"provider\"></p-radioButton>\n <label>Open AI</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"google\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Google</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openrouter\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Open Router</label>\n </div>\n </div>\n\n <b>Modelo: </b>\n <span pTooltip=\"Modelo Seleccionado\">{{ parentForm.controls.modelName.value }}</span>\n @if (parentForm.controls.provider.value) { @if(isLoadingModels) {\n <p-skeleton height=\"200px\" width=\"100%\"></p-skeleton>\n } @else {\n <p-table [value]=\"modelnames\" stripedRows [size]=\"'small'\" [paginator]=\"true\" [rows]=\"12\" [formGroup]=\"parentForm\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th></th>\n <th>Name</th>\n <th>$$Prompt M</th>\n <th>$$Completion M</th>\n <th>$$Cost 90/10 M</th>\n <th>Created</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-model>\n <tr [pTooltip]=\"model.description | truncate : 200\" tooltipPosition=\"top\">\n <td><p-radioButton [value]=\"model.id\" formControlName=\"modelName\"></p-radioButton></td>\n <td>{{ model.name }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.completion * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 * 0.9 + +model.pricing?.completion * 1000000 * 0.1 | number : '1.2-2' }}</td>\n <td>{{ model.created * 1000 | date : 'dd/MM/yyyy' }}</td>\n </tr>\n </ng-template>\n </p-table>\n } }\n</div>\n", styles: [":host{display:block}.space{display:flex;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type:
|
|
2049
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ProviderSelectorComponent, isStandalone: true, selector: "dc-provider-selector", inputs: { parentForm: "parentForm" }, ngImport: i0, template: "<div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Providers:</b>\n <div style=\"display: flex; gap: 10px\" [formGroup]=\"parentForm\">\n <div class=\"space\">\n <p-radioButton value=\"groq\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Groq</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openai\" formControlName=\"provider\"></p-radioButton>\n <label>Open AI</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"google\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Google</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openrouter\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Open Router</label>\n </div>\n </div>\n\n <b>Modelo: </b>\n <span pTooltip=\"Modelo Seleccionado\">{{ parentForm.controls.modelName.value }}</span>\n @if (parentForm.controls.provider.value) { @if(isLoadingModels) {\n <p-skeleton height=\"200px\" width=\"100%\"></p-skeleton>\n } @else {\n <p-table [value]=\"modelnames\" stripedRows [size]=\"'small'\" [paginator]=\"true\" [rows]=\"12\" [formGroup]=\"parentForm\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th></th>\n <th>Name</th>\n <th>$$Prompt M</th>\n <th>$$Completion M</th>\n <th>$$Cost 90/10 M</th>\n <th>Created</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-model>\n <tr [pTooltip]=\"model.description | truncate : 200\" tooltipPosition=\"top\">\n <td><p-radioButton [value]=\"model.id\" formControlName=\"modelName\"></p-radioButton></td>\n <td>{{ model.name }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.completion * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 * 0.9 + +model.pricing?.completion * 1000000 * 0.1 | number : '1.2-2' }}</td>\n <td>{{ model.created * 1000 | date : 'dd/MM/yyyy' }}</td>\n </tr>\n </ng-template>\n </p-table>\n } }\n</div>\n", styles: [":host{display:block}.space{display:flex;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$2.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i3$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i5.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i4.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "pipe", type: TruncatePipe, name: "truncate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1353
2050
|
}
|
|
1354
2051
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ProviderSelectorComponent, decorators: [{
|
|
1355
2052
|
type: Component,
|
|
@@ -1399,85 +2096,308 @@ class DCConversationUserChatSettingsComponent {
|
|
|
1399
2096
|
});
|
|
1400
2097
|
this.isAdmin = true;
|
|
1401
2098
|
}
|
|
1402
|
-
ngOnInit() {
|
|
1403
|
-
console.log('Settings form initialized', this.form.value, this.form.controls.highlightWords);
|
|
1404
|
-
this.loadInitialSettings();
|
|
1405
|
-
this.form.valueChanges.subscribe((value) => {
|
|
1406
|
-
this.onSettingsChange.emit(value);
|
|
1407
|
-
});
|
|
2099
|
+
ngOnInit() {
|
|
2100
|
+
console.log('Settings form initialized', this.form.value, this.form.controls.highlightWords);
|
|
2101
|
+
this.loadInitialSettings();
|
|
2102
|
+
this.form.valueChanges.subscribe((value) => {
|
|
2103
|
+
this.onSettingsChange.emit(value);
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
async loadInitialSettings() {
|
|
2107
|
+
const settings = await this.conversationCardAIService.getConversationUserChatSettings();
|
|
2108
|
+
if (settings) {
|
|
2109
|
+
this.form.patchValue(settings);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
close() {
|
|
2113
|
+
this.dialogRef.close();
|
|
2114
|
+
}
|
|
2115
|
+
async saveSettings() {
|
|
2116
|
+
if (this.form.valid) {
|
|
2117
|
+
await this.conversationCardAIService.saveConversationUserChatSettings(this.form.value);
|
|
2118
|
+
this.dialogRef.close(this.form.value);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [{ token: i1$3.DynamicDialogRef }, { token: i1.FormBuilder }, { token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2122
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCConversationUserChatSettingsComponent, isStandalone: true, selector: "dc-chat-settings-dialog", inputs: { showFeature: "showFeature" }, outputs: { onSettingsChange: "onSettingsChange" }, viewQueries: [{ propertyName: "tooltipRef", first: true, predicate: ["tooltipRef"], descendants: true }], ngImport: i0, template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "directive", type: i4$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: SliderModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i3$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.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: "pipe", type: SpeedDescPipe, name: "speedDisplay" }, { kind: "ngmodule", type: RatingModule }, { kind: "component", type: i8.Rating, selector: "p-rating", inputs: ["disabled", "readonly", "stars", "iconOnClass", "iconOnStyle", "iconOffClass", "iconOffStyle", "autofocus"], outputs: ["onRate", "onCancel", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }] }); }
|
|
2123
|
+
}
|
|
2124
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
|
|
2125
|
+
type: Component,
|
|
2126
|
+
args: [{ selector: 'dc-chat-settings-dialog', standalone: true, imports: [
|
|
2127
|
+
CommonModule,
|
|
2128
|
+
ReactiveFormsModule,
|
|
2129
|
+
CheckboxModule,
|
|
2130
|
+
SliderModule,
|
|
2131
|
+
RadioButtonModule,
|
|
2132
|
+
ButtonModule,
|
|
2133
|
+
SpeedDescPipe,
|
|
2134
|
+
RatingModule,
|
|
2135
|
+
TableModule,
|
|
2136
|
+
BadgeModule,
|
|
2137
|
+
SkeletonModule,
|
|
2138
|
+
TooltipModule,
|
|
2139
|
+
ProviderSelectorComponent,
|
|
2140
|
+
], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"] }]
|
|
2141
|
+
}], ctorParameters: () => [{ type: i1$3.DynamicDialogRef }, { type: i1.FormBuilder }, { type: AgentCardsAbstractService, decorators: [{
|
|
2142
|
+
type: Inject,
|
|
2143
|
+
args: [CONVERSATION_AI_TOKEN]
|
|
2144
|
+
}] }], propDecorators: { showFeature: [{
|
|
2145
|
+
type: Input
|
|
2146
|
+
}], onSettingsChange: [{
|
|
2147
|
+
type: Output
|
|
2148
|
+
}], tooltipRef: [{
|
|
2149
|
+
type: ViewChild,
|
|
2150
|
+
args: ['tooltipRef']
|
|
2151
|
+
}] } });
|
|
2152
|
+
|
|
2153
|
+
const EvalResultStringDefinition = `
|
|
2154
|
+
interface EvalResult {
|
|
2155
|
+
score: number; // Score of the user's response 0 to 3
|
|
2156
|
+
feedback: string; // Feedback of the user's understanding of the conversation
|
|
2157
|
+
}`;
|
|
2158
|
+
const DefaultEvaluatorAgentCard = {
|
|
2159
|
+
task: 'Evaluate the user understanding of the lesson',
|
|
2160
|
+
messages: [],
|
|
2161
|
+
expectedResponseType: EvalResultStringDefinition,
|
|
2162
|
+
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
2163
|
+
sources: [],
|
|
2164
|
+
};
|
|
2165
|
+
|
|
2166
|
+
function extractJsonFromResponse(content) {
|
|
2167
|
+
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
2168
|
+
if (!jsonMatch)
|
|
2169
|
+
return null;
|
|
2170
|
+
try {
|
|
2171
|
+
return JSON.parse(jsonMatch[0]);
|
|
2172
|
+
}
|
|
2173
|
+
catch (error) {
|
|
2174
|
+
console.error('Error parsing JSON:', error);
|
|
2175
|
+
return null;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
class EvaluationService {
|
|
2180
|
+
constructor(agentCardService) {
|
|
2181
|
+
this.agentCardService = agentCardService;
|
|
2182
|
+
this.scoreSignal = signal(10);
|
|
2183
|
+
this.evaluationResultSignal = signal(null);
|
|
2184
|
+
}
|
|
2185
|
+
// Get score as a signal
|
|
2186
|
+
getScore() {
|
|
2187
|
+
return this.scoreSignal;
|
|
2188
|
+
}
|
|
2189
|
+
// Get evaluation result as a signal
|
|
2190
|
+
getEvaluationResult() {
|
|
2191
|
+
return this.evaluationResultSignal;
|
|
2192
|
+
}
|
|
2193
|
+
// Evaluate conversation
|
|
2194
|
+
async evaluateConversation(messages, evaluator) {
|
|
2195
|
+
// Filter conversation to only include user and assistant messages
|
|
2196
|
+
const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
2197
|
+
// Check if there are any user messages to evaluate
|
|
2198
|
+
const userMessages = conversationMessages.filter((message) => message.role === ChatRole.User);
|
|
2199
|
+
if (userMessages.length <= 0) {
|
|
2200
|
+
console.log('No messages to evaluate', conversationMessages);
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
// Format conversation for evaluation
|
|
2204
|
+
const conversationMessagesString = conversationMessages
|
|
2205
|
+
.map((message) => `${message.role}: ${message.content}`)
|
|
2206
|
+
.join('\n');
|
|
2207
|
+
// Create evaluation prompt
|
|
2208
|
+
const instructions = `
|
|
2209
|
+
Please replay to this task:
|
|
2210
|
+
${evaluator.task}
|
|
2211
|
+
This is the conversation history:
|
|
2212
|
+
${conversationMessagesString}
|
|
2213
|
+
and give the response in next JSON format.
|
|
2214
|
+
${evaluator.expectedResponseType}
|
|
2215
|
+
`;
|
|
2216
|
+
// Send evaluation request
|
|
2217
|
+
const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
|
|
2218
|
+
const response = await this.agentCardService.callChatCompletion({ messages: evaluationMessages });
|
|
2219
|
+
// Extract JSON from response
|
|
2220
|
+
const jsonData = extractJsonFromResponse(response.content);
|
|
2221
|
+
this.evaluationResultSignal.set(jsonData);
|
|
2222
|
+
// Update score if available
|
|
2223
|
+
if (jsonData.score) {
|
|
2224
|
+
if (jsonData.score <= 3) {
|
|
2225
|
+
this.updateScore(jsonData.score * 10);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
// Update score with limits
|
|
2230
|
+
updateScore(additionalScore) {
|
|
2231
|
+
this.scoreSignal.update(currentScore => {
|
|
2232
|
+
const newScore = currentScore + additionalScore;
|
|
2233
|
+
return newScore > 100 ? 100 : newScore;
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
// Reset score
|
|
2237
|
+
resetScore() {
|
|
2238
|
+
this.scoreSignal.set(10);
|
|
2239
|
+
}
|
|
2240
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: EvaluationService, deps: [{ token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2241
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: EvaluationService, providedIn: 'root' }); }
|
|
2242
|
+
}
|
|
2243
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: EvaluationService, decorators: [{
|
|
2244
|
+
type: Injectable,
|
|
2245
|
+
args: [{
|
|
2246
|
+
providedIn: 'root',
|
|
2247
|
+
}]
|
|
2248
|
+
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
2249
|
+
type: Inject,
|
|
2250
|
+
args: [CONVERSATION_AI_TOKEN]
|
|
2251
|
+
}] }] });
|
|
2252
|
+
|
|
2253
|
+
class ChatContainerComponent {
|
|
2254
|
+
constructor(agentCardService, toastService, conversationBuilder, dialogService, conversationService, evaluationService) {
|
|
2255
|
+
this.agentCardService = agentCardService;
|
|
2256
|
+
this.toastService = toastService;
|
|
2257
|
+
this.conversationBuilder = conversationBuilder;
|
|
2258
|
+
this.dialogService = dialogService;
|
|
2259
|
+
this.conversationService = conversationService;
|
|
2260
|
+
this.evaluationService = evaluationService;
|
|
2261
|
+
this.evaluatorAgentCard = DefaultEvaluatorAgentCard;
|
|
2262
|
+
this.parseDict = {};
|
|
2263
|
+
this.sendMessage = new EventEmitter();
|
|
2264
|
+
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
2265
|
+
this.imageUser = `assets/default/user.svg`;
|
|
2266
|
+
this.thinkingImg = `assets/default/thinking.svg`;
|
|
2267
|
+
this.aiIcon = `assets/default/ai.svg`;
|
|
2268
|
+
this.isInfoVisible = false;
|
|
2269
|
+
this.isChatSettingsVisible = false;
|
|
2270
|
+
this.isUserTalking = false;
|
|
2271
|
+
this.isGettingTranscription = false;
|
|
2272
|
+
this.isAdmin = true;
|
|
2273
|
+
// Get reactive state from services
|
|
2274
|
+
this.messages = this.conversationService.getMessages();
|
|
2275
|
+
this.isThinking = this.conversationService.isThinking();
|
|
2276
|
+
this.score = this.evaluationService.getScore();
|
|
2277
|
+
}
|
|
2278
|
+
async ngOnInit() {
|
|
2279
|
+
debugger;
|
|
2280
|
+
// Get user settings if not provided
|
|
2281
|
+
if (!this.chatUserSettings) {
|
|
2282
|
+
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2283
|
+
}
|
|
2284
|
+
// Initialize conversation
|
|
2285
|
+
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict);
|
|
1408
2286
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
2287
|
+
ngOnDestroy() {
|
|
2288
|
+
// Mark conversation as destroyed to prevent async operations
|
|
2289
|
+
this.conversationService.setDestroyed(true);
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Handle user message from chat footer
|
|
2293
|
+
*/
|
|
2294
|
+
async onUserMessage(message) {
|
|
2295
|
+
if (this.isThinking()) {
|
|
2296
|
+
return;
|
|
1413
2297
|
}
|
|
2298
|
+
await this.conversationService.sendUserMessage(message);
|
|
2299
|
+
// Evaluate conversation after sending message
|
|
2300
|
+
this.evaluateConversation();
|
|
1414
2301
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
2302
|
+
/**
|
|
2303
|
+
* Handle microphone input finished
|
|
2304
|
+
*/
|
|
2305
|
+
async onMicFinished(eventBlob) {
|
|
2306
|
+
if (!(eventBlob instanceof Blob)) {
|
|
2307
|
+
if (!this.chatUserSettings.superHearing) {
|
|
2308
|
+
this.isUserTalking = false;
|
|
2309
|
+
}
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
// Check if audio is too small
|
|
2313
|
+
if (eventBlob.size < 10000) {
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
this.isUserTalking = false;
|
|
2317
|
+
this.isGettingTranscription = true;
|
|
2318
|
+
try {
|
|
2319
|
+
// Get transcription from audio
|
|
2320
|
+
const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, {
|
|
2321
|
+
conversationId: this.agentCard._id,
|
|
2322
|
+
});
|
|
2323
|
+
// Create message with transcription
|
|
2324
|
+
const message = {
|
|
2325
|
+
content: transcription.text,
|
|
2326
|
+
role: ChatRole.User,
|
|
2327
|
+
transcriptionTimestamps: transcription.words,
|
|
2328
|
+
};
|
|
2329
|
+
message['audioUrl'] = URL.createObjectURL(eventBlob);
|
|
2330
|
+
// Send message to conversation
|
|
2331
|
+
await this.conversationService.sendUserMessage(message);
|
|
2332
|
+
// Evaluate conversation
|
|
2333
|
+
this.evaluateConversation();
|
|
2334
|
+
}
|
|
2335
|
+
finally {
|
|
2336
|
+
this.isGettingTranscription = false;
|
|
2337
|
+
}
|
|
1417
2338
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
2339
|
+
/**
|
|
2340
|
+
* Open chat settings dialog
|
|
2341
|
+
*/
|
|
2342
|
+
changeUserChatSettings() {
|
|
2343
|
+
this.dialogService
|
|
2344
|
+
.open(DCConversationUserChatSettingsComponent, {
|
|
2345
|
+
width: '90vw',
|
|
2346
|
+
header: 'Chat Settings',
|
|
2347
|
+
styleClass: 'settings-dialog',
|
|
2348
|
+
closable: true,
|
|
2349
|
+
closeOnEscape: true,
|
|
2350
|
+
})
|
|
2351
|
+
.onClose.subscribe(async () => {
|
|
2352
|
+
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* Show debug info
|
|
2357
|
+
*/
|
|
2358
|
+
showInfo() {
|
|
2359
|
+
this.isInfoVisible = true;
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Restart conversation
|
|
2363
|
+
*/
|
|
2364
|
+
async restartConversation(conversation = null) {
|
|
2365
|
+
if (conversation) {
|
|
2366
|
+
this.agentCard = conversation;
|
|
1422
2367
|
}
|
|
2368
|
+
await this.ngOnInit();
|
|
2369
|
+
}
|
|
2370
|
+
/**
|
|
2371
|
+
* Evaluate conversation using evaluator agent
|
|
2372
|
+
*/
|
|
2373
|
+
async evaluateConversation() {
|
|
2374
|
+
const messages = this.messages();
|
|
2375
|
+
await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard);
|
|
1423
2376
|
}
|
|
1424
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type:
|
|
1425
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
2377
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatContainerComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: DCConversationPromptBuilderService }, { token: i1$3.DialogService }, { token: ConversationService }, { token: EvaluationService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2378
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ChatContainerComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: "chatUserSettings", agentCard: "agentCard", evaluatorAgentCard: "evaluatorAgentCard", parseDict: "parseDict" }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [isAIThinking]=\"isThinking()\" [micSettings]=\"micSettings\" (sendMessage)=\"onUserMessage($event)\" (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <div class=\"score-container\" *ngIf=\"score() > 0\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:var(--surface-ground, #f8f9fa)}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: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "score", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "thinkingImg"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i6.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: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1426
2379
|
}
|
|
1427
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type:
|
|
2380
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatContainerComponent, decorators: [{
|
|
1428
2381
|
type: Component,
|
|
1429
|
-
args: [{ selector: 'dc-chat
|
|
1430
|
-
|
|
1431
|
-
ReactiveFormsModule,
|
|
1432
|
-
CheckboxModule,
|
|
1433
|
-
SliderModule,
|
|
1434
|
-
RadioButtonModule,
|
|
1435
|
-
ButtonModule,
|
|
1436
|
-
SpeedDescPipe,
|
|
1437
|
-
RatingModule,
|
|
1438
|
-
TableModule,
|
|
1439
|
-
BadgeModule,
|
|
1440
|
-
SkeletonModule,
|
|
1441
|
-
TooltipModule,
|
|
1442
|
-
ProviderSelectorComponent,
|
|
1443
|
-
], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"] }]
|
|
1444
|
-
}], ctorParameters: () => [{ type: i1$2.DynamicDialogRef }, { type: i1$1.FormBuilder }, { type: AgentCardsAbstractService, decorators: [{
|
|
2382
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [isAIThinking]=\"isThinking()\" [micSettings]=\"micSettings\" (sendMessage)=\"onUserMessage($event)\" (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <div class=\"score-container\" *ngIf=\"score() > 0\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:var(--surface-ground, #f8f9fa)}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"] }]
|
|
2383
|
+
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
1445
2384
|
type: Inject,
|
|
1446
2385
|
args: [CONVERSATION_AI_TOKEN]
|
|
1447
|
-
}] }
|
|
2386
|
+
}] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
2387
|
+
type: Inject,
|
|
2388
|
+
args: [TOAST_ALERTS_TOKEN]
|
|
2389
|
+
}] }, { type: DCConversationPromptBuilderService }, { type: i1$3.DialogService }, { type: ConversationService }, { type: EvaluationService }], propDecorators: { chatUserSettings: [{
|
|
1448
2390
|
type: Input
|
|
1449
|
-
}],
|
|
2391
|
+
}], agentCard: [{
|
|
2392
|
+
type: Input
|
|
2393
|
+
}], evaluatorAgentCard: [{
|
|
2394
|
+
type: Input
|
|
2395
|
+
}], parseDict: [{
|
|
2396
|
+
type: Input
|
|
2397
|
+
}], sendMessage: [{
|
|
1450
2398
|
type: Output
|
|
1451
|
-
}], tooltipRef: [{
|
|
1452
|
-
type: ViewChild,
|
|
1453
|
-
args: ['tooltipRef']
|
|
1454
2399
|
}] } });
|
|
1455
2400
|
|
|
1456
|
-
function extractJsonFromResponse(content) {
|
|
1457
|
-
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
1458
|
-
if (!jsonMatch)
|
|
1459
|
-
return null;
|
|
1460
|
-
try {
|
|
1461
|
-
return JSON.parse(jsonMatch[0]);
|
|
1462
|
-
}
|
|
1463
|
-
catch (error) {
|
|
1464
|
-
console.error('Error parsing JSON:', error);
|
|
1465
|
-
return null;
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
const EvalResultStringDefinition = `
|
|
1470
|
-
interface EvalResult {
|
|
1471
|
-
score: number; // Score of the user's response 0 to 3
|
|
1472
|
-
feedback: string; // Feedback of the user's understanding of the conversation
|
|
1473
|
-
}`;
|
|
1474
|
-
const DefaultEvaluatorAgentCard = {
|
|
1475
|
-
task: 'Evaluate the user understanding of the lesson',
|
|
1476
|
-
messages: [],
|
|
1477
|
-
expectedResponseType: EvalResultStringDefinition,
|
|
1478
|
-
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
1479
|
-
sources: [],
|
|
1480
|
-
};
|
|
1481
2401
|
class DCChatComponent {
|
|
1482
2402
|
constructor(agentCardService, toastService, conversationBuilder, dialogService, cdr) {
|
|
1483
2403
|
this.agentCardService = agentCardService;
|
|
@@ -1488,40 +2408,19 @@ class DCChatComponent {
|
|
|
1488
2408
|
this.sendMessage = new EventEmitter();
|
|
1489
2409
|
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
1490
2410
|
this.messages = [];
|
|
1491
|
-
this.imageUser = `
|
|
1492
|
-
this.thinkingImg = `
|
|
2411
|
+
this.imageUser = `assets/default/user.svg`;
|
|
2412
|
+
this.thinkingImg = `assets/default/thinking.svg`;
|
|
2413
|
+
this.aiIcon = `assets/default/ai.svg`;
|
|
1493
2414
|
this.isDestroyed = false;
|
|
1494
2415
|
this.score = 10;
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
// @ViewChild(MicComponent) micComponent: MicComponent;
|
|
2416
|
+
this.isAdmin = true;
|
|
2417
|
+
this.isInfoVisible = false;
|
|
1498
2418
|
this.isChatSettingsVisible = false;
|
|
1499
|
-
|
|
2419
|
+
// chatInputControl moved to ChatFooterComponent
|
|
1500
2420
|
this.isAIThinking = false;
|
|
1501
2421
|
this.isUserTalking = false;
|
|
1502
|
-
this.aiIcon = `data:image/svg+xml;base64,${btoa(decodeURIComponent(ICONS.ai))}`;
|
|
1503
2422
|
this.user = null; // TODO: remove user.
|
|
1504
2423
|
this.isGettingTranscription = false;
|
|
1505
|
-
// public fakeConversation() {
|
|
1506
|
-
// const currentConversation = this.messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
1507
|
-
// const index = currentConversation.length / 2 - 1;
|
|
1508
|
-
// const message = { content: this.chatboxService.fakeConversations[index], role: ChatRole.Assistant };
|
|
1509
|
-
// this.isAIThinking = true;
|
|
1510
|
-
// setTimeout(() => {
|
|
1511
|
-
// this.messages.push(message);
|
|
1512
|
-
// // this.speechTextAndAttachAudio(message);
|
|
1513
|
-
// }, 700);
|
|
1514
|
-
// return;
|
|
1515
|
-
// }
|
|
1516
|
-
// public updateSettings() {
|
|
1517
|
-
// // it is updated by subscription when user is updated
|
|
1518
|
-
// // this.conversationSettings = this.userService.getUserSnapshot().settings.conversation;
|
|
1519
|
-
// }
|
|
1520
|
-
// private async getConversationEvaluation() {
|
|
1521
|
-
// // Obtiene solo los mensajes de la historia.
|
|
1522
|
-
// const messagesFiltered = this.messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
1523
|
-
// const scenario_challenge = this.chatboxService.item['card']['scenario'];
|
|
1524
|
-
this.isInfoVisible = false;
|
|
1525
2424
|
}
|
|
1526
2425
|
async ngOnInit() {
|
|
1527
2426
|
if (!this.agentCard?.conversationSettings) {
|
|
@@ -1543,9 +2442,6 @@ class DCChatComponent {
|
|
|
1543
2442
|
const firstAssistantMsn = this.messages.find((message) => message.role == ChatRole.Assistant);
|
|
1544
2443
|
if (firstAssistantMsn) {
|
|
1545
2444
|
if (this.chatUserSettings?.autoTranslate) {
|
|
1546
|
-
// this.aiService.translate(firstAssistantMsn.content).then((res: any) => {
|
|
1547
|
-
// firstAssistantMsn.translation = res?.translation;
|
|
1548
|
-
// });
|
|
1549
2445
|
}
|
|
1550
2446
|
this.buildChatMessage(firstAssistantMsn, true);
|
|
1551
2447
|
}
|
|
@@ -1553,12 +2449,6 @@ class DCChatComponent {
|
|
|
1553
2449
|
this.sendCurrentConversation();
|
|
1554
2450
|
}
|
|
1555
2451
|
}
|
|
1556
|
-
// onSendMessage(): void {
|
|
1557
|
-
// if (this.userMessage?.trim()) {
|
|
1558
|
-
// this.sendMessage.emit(this.userMessage);
|
|
1559
|
-
// this.userMessage = '';
|
|
1560
|
-
// }
|
|
1561
|
-
// }
|
|
1562
2452
|
buildChatMessage(message, mutate = false) {
|
|
1563
2453
|
// De la respuesta del backend configura el mensaje para ser rendiriado y procesado en la interfaz
|
|
1564
2454
|
if (mutate) {
|
|
@@ -1579,7 +2469,7 @@ class DCChatComponent {
|
|
|
1579
2469
|
}
|
|
1580
2470
|
return message;
|
|
1581
2471
|
}
|
|
1582
|
-
|
|
2472
|
+
changeUserChatSettings() {
|
|
1583
2473
|
this.dialogService
|
|
1584
2474
|
.open(DCConversationUserChatSettingsComponent, {
|
|
1585
2475
|
width: '90vw',
|
|
@@ -1612,28 +2502,12 @@ class DCChatComponent {
|
|
|
1612
2502
|
if (this.isAIThinking) {
|
|
1613
2503
|
return;
|
|
1614
2504
|
}
|
|
1615
|
-
if (!message
|
|
1616
|
-
|
|
1617
|
-
if (!text) {
|
|
1618
|
-
return;
|
|
1619
|
-
}
|
|
1620
|
-
message = { content: text, role: ChatRole.User };
|
|
2505
|
+
if (!message) {
|
|
2506
|
+
return;
|
|
1621
2507
|
}
|
|
1622
2508
|
this.messages.push(message); // This activates the render of the message
|
|
1623
|
-
// ESTO debe ser una especie de plugin.
|
|
1624
|
-
// if (this.conversationSettings?.fixGrammar && this.chatboxService.settings.conversationType == ConversationType.Scenario) {
|
|
1625
|
-
// // call api to improve the text
|
|
1626
|
-
// this.aiService.callInstruction(message.content).then((res) => {
|
|
1627
|
-
// this.messages.push({ content: res.text, role: ChatRole.AssistantHelper });
|
|
1628
|
-
// });
|
|
1629
|
-
// }
|
|
1630
|
-
this.chatInputControl.setValue('');
|
|
1631
2509
|
this.setAIthinking();
|
|
1632
2510
|
try {
|
|
1633
|
-
// if (this.chatboxService.fakeConversations.length > 0) {
|
|
1634
|
-
// this.fakeConversation();
|
|
1635
|
-
// return;
|
|
1636
|
-
// }
|
|
1637
2511
|
if (message.audioUrl && this.chatUserSettings.repeatRecording) {
|
|
1638
2512
|
// Esto significa que el usuario grabo un audio y tengo que esperar a que termine para seguir con la conversación
|
|
1639
2513
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -1681,20 +2555,12 @@ class DCChatComponent {
|
|
|
1681
2555
|
}
|
|
1682
2556
|
console.log('newMessage', response);
|
|
1683
2557
|
const newMessage = this.buildChatMessage(response);
|
|
1684
|
-
//
|
|
1685
|
-
// this.aiService.translate(newMessage.content).then((res: any) => {
|
|
1686
|
-
// newMessage.translation = res?.translation;
|
|
1687
|
-
// });
|
|
1688
|
-
// }
|
|
2558
|
+
// TODO: think on how to include this as pluggin. this.conversationSettings?.autoTranslate
|
|
1689
2559
|
this.messages.push(newMessage); // This point ChatMessage will be rendered then ngOnInit will handle everything.
|
|
1690
2560
|
// Aqui se debe evaluar si el mensaje es un evaluador, si es así, se debe llamar a la función de evaluación.
|
|
1691
2561
|
this.evaluateConversation();
|
|
1692
2562
|
this.isAIThinking = false;
|
|
1693
2563
|
this.cdr.detectChanges();
|
|
1694
|
-
// if (this.scenarioType == ScenarioType.Challenge) {
|
|
1695
|
-
// console.log('challenge');
|
|
1696
|
-
// this.getConversationEvaluation();
|
|
1697
|
-
// }
|
|
1698
2564
|
if (this.chatUserSettings.realTime) {
|
|
1699
2565
|
if (this.isDestroyed)
|
|
1700
2566
|
return;
|
|
@@ -1729,7 +2595,8 @@ class DCChatComponent {
|
|
|
1729
2595
|
}
|
|
1730
2596
|
}
|
|
1731
2597
|
setInputText(text) {
|
|
1732
|
-
|
|
2598
|
+
// Method now receives text from the chat-footer component
|
|
2599
|
+
// No need to set the value as it's handled in the child component
|
|
1733
2600
|
}
|
|
1734
2601
|
async micFinished(eventBlob) {
|
|
1735
2602
|
if (eventBlob instanceof Blob) {
|
|
@@ -1759,25 +2626,7 @@ class DCChatComponent {
|
|
|
1759
2626
|
// // when you click on profile this is activated
|
|
1760
2627
|
playMessage(message) {
|
|
1761
2628
|
console.log('playMessage', message);
|
|
1762
|
-
// if (message.audioUrl) {
|
|
1763
|
-
// this.audioService.playAudio(message.audioUrl);
|
|
1764
|
-
// } else {
|
|
1765
|
-
// this.speechService.speach(message.content);
|
|
1766
|
-
// }
|
|
1767
2629
|
}
|
|
1768
|
-
// public restartConversation() {
|
|
1769
|
-
// this.messages = [];
|
|
1770
|
-
// this.messages.length;
|
|
1771
|
-
// }
|
|
1772
|
-
// public someTextSelected(event) {
|
|
1773
|
-
// console.log('someTextSelected', event);
|
|
1774
|
-
// this.wordMenuService.showMenu(event.position, event.text);
|
|
1775
|
-
// }
|
|
1776
|
-
// public userTalking() {
|
|
1777
|
-
// if (this.conversationSettings.superHearing) {
|
|
1778
|
-
// this.isUserTalking = true;
|
|
1779
|
-
// }
|
|
1780
|
-
// }
|
|
1781
2630
|
subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
|
|
1782
2631
|
const regex = /\*(.*?)\*/g;
|
|
1783
2632
|
return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
|
|
@@ -1803,24 +2652,9 @@ class DCChatComponent {
|
|
|
1803
2652
|
console.log('chatUserSettings', this.chatUserSettings);
|
|
1804
2653
|
this.isInfoVisible = true;
|
|
1805
2654
|
}
|
|
1806
|
-
async changeConversationCard() {
|
|
1807
|
-
const response = prompt('¿Qué conversación quieres usar? Escribe el titulo ejemplo: word_reflection_level_1_base_es');
|
|
1808
|
-
if (response) {
|
|
1809
|
-
const filters = {
|
|
1810
|
-
filters: {
|
|
1811
|
-
title: { $regex: response },
|
|
1812
|
-
},
|
|
1813
|
-
};
|
|
1814
|
-
console.log('filters', filters);
|
|
1815
|
-
const conversationCards = await this.agentCardService.filterConversationCards(filters);
|
|
1816
|
-
console.log('conversationCards', conversationCards);
|
|
1817
|
-
this.alternativeConversation = conversationCards.rows;
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
2655
|
async restartConversation(conversation = null) {
|
|
1821
2656
|
if (conversation) {
|
|
1822
2657
|
this.agentCard = conversation;
|
|
1823
|
-
// this.conversationSettings = this.promptBuilder.buildConversationSettings(this.conversationCard, this.parseDict);
|
|
1824
2658
|
}
|
|
1825
2659
|
await this.ngOnInit();
|
|
1826
2660
|
}
|
|
@@ -1867,19 +2701,29 @@ and give the response in next JSON format.
|
|
|
1867
2701
|
}
|
|
1868
2702
|
console.log('response', response);
|
|
1869
2703
|
}
|
|
1870
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCChatComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: DCConversationPromptBuilderService }, { token: i1$
|
|
1871
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: "chatUserSettings", agentCard: "agentCard", evaluatorAgentCard: "evaluatorAgentCard", parseDict: "parseDict" }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<
|
|
2704
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCChatComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: DCConversationPromptBuilderService }, { token: i1$3.DialogService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2705
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: "chatUserSettings", agentCard: "agentCard", evaluatorAgentCard: "evaluatorAgentCard", parseDict: "parseDict" }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<dc-chat-header\n [isAdmin]=\"isAdmin\"\n [alternativeConversation]=\"alternativeConversation\"\n (restartConversationEvent)=\"restartConversation($event || null)\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\">\n</dc-chat-header>\n\n<main class=\"chat-container\" (touchmove)=\"$event.stopPropagation()\">\n <div class=\"chat\" aria-live=\"polite\">\n @for (message of messages; track message) { @switch (message.role) { @case ('assistant') {\n <div class=\"message left\">\n <img class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? imageUser\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('assistantHelper') {\n <div class=\"message-helper right\">\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('user') {\n <div class=\"message right\">\n <img class=\"logo fit-top-image\" [src]=\"user?.urlPicture || 'assets/defaults/avatar.jpg'\" [alt]=\"'User avatar'\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } } }\n\n <!-- AI thinking indicator -->\n @if (isAIThinking) {\n <div class=\"message message--assistant message--thinking\">\n <span class=\"thinking-emoji\" aria-hidden=\"true\">\uD83E\uDD14</span>\n <div class=\"thinking-indicator\">\n <div class=\"skeleton-container\">\n <p-skeleton width=\"100%\" />\n <br />\n <p-skeleton width=\"75%\" />\n </div>\n </div>\n </div>\n }\n\n <!-- User typing or transcribing indicator -->\n @if (isUserTalking || isGettingTranscription) {\n <div class=\"message message--user message--activity\">\n <img class=\"avatar avatar--user\" [src]=\"user?.urlPicture || imageUser\" [alt]=\"'User avatar'\" />\n @if (isUserTalking) {\n <p class=\"typing-indicator\" aria-label=\"User is speaking\">...</p>\n } @if (isGettingTranscription) {\n <i class=\"pi pi-spin pi-spinner-dotted\" aria-label=\"Transcribing speech\"></i>\n }\n </div>\n }\n </div>\n</main>\n\n<dc-chat-footer\n [isAIThinking]=\"isAIThinking\"\n [score]=\"score\"\n [micSettings]=\"micSettings\"\n (sendMessage)=\"sendUserMessage($event)\"\n (textInputChanged)=\"setInputText($event)\"\n (micFinishedEvent)=\"micFinished($event)\">\n</dc-chat-footer>\n\n<!-- Information dialog -->\n<p-dialog\n header=\"Informaci\u00F3n de la conversaci\u00F3n\"\n [(visible)]=\"isInfoVisible\"\n [modal]=\"true\"\n [responsive]=\"true\"\n styleClass=\"conversation-info-dialog\"\n aria-labelledby=\"conversation-info-title\">\n <div class=\"dialog-content\">\n <section class=\"model-info\">\n <h3 id=\"conversation-info-title\" class=\"sr-only\">Informaci\u00F3n de la conversaci\u00F3n</h3>\n <dl class=\"info-list\">\n <dt>Modelo:</dt>\n <dd>{{ chatUserSettings?.model?.provider }} - {{ chatUserSettings?.model?.modelName }}</dd>\n\n <dt>Tipo:</dt>\n <dd>{{ agentCard?.conversationSettings?.conversationType }}</dd>\n\n <dt>Text Engine:</dt>\n <dd>{{ agentCard?.conversationSettings?.textEngine }}</dd>\n\n <dt>Language:</dt>\n <dd>{{ agentCard?.lang }}</dd>\n\n <dt>TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.voice }}</dd>\n\n <dt>Secondary TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.secondaryVoice }}</dd>\n </dl>\n\n <div class=\"parameters\">\n <h4>Par\u00E1metros:</h4>\n <pre>{{ parseDict | json }}</pre>\n </div>\n </section>\n\n <section class=\"conversation-history\">\n <h4>Conversaci\u00F3n hasta ahora</h4>\n <div class=\"message-history-list\">\n @for (message of conversationSettings.messages; track message) {\n <div class=\"message-history-item\">\n <span class=\"message-role\">{{ message.role }}:</span>\n <span class=\"message-content\">{{ message.content }}</span>\n </div>\n }\n </div>\n </section>\n </div>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%}*{box-sizing:border-box}.chat-container{border-radius:10px;overflow:hidden;padding:15px;position:relative;max-width:100%;height:100%;display:flex;flex-direction:column}.chat{display:flex;flex-direction:column;list-style-type:none;padding:0;margin:0;overflow-y:auto;flex:1}.logo{width:30px;height:30px}.logo.fit-top-image{object-fit:cover;object-position:top;border-radius:50%}.message{background-color:#f6f6f6f2;border-radius:25px;box-shadow:0 5px 15px #0000001a;position:relative;margin-bottom:20px;transition:all .2s ease}.message .logo{position:absolute;top:50%;transform:translateY(-50%);border-radius:50%;cursor:pointer}.message .logo:hover{transform:translateY(-50%) scale(1.05)}.message.left{padding:12px 15px 12px 45px;margin-right:20%}.message.left .logo{left:5px}.message.right{align-self:flex-end;padding:12px 45px 12px 15px;margin-left:20%;background-color:#e6f0fff2}.message.right .logo{right:5px}.message--thinking,.message--activity{opacity:.7}.message-helper{background-color:#ffffffe6;box-shadow:0 3px 10px #0000001a;position:relative;margin-bottom:20px;border-radius:20px}.message-helper.right{align-self:flex-end;padding:8px 15px;margin-left:20%}.thinking-indicator{padding:5px}.thinking-indicator .skeleton-container{width:200px}::ng-deep .cdk-overlay-container,::ng-deep .cdk-global-overlay-wrapper{z-index:1400}::ng-deep .dialog-backdrop{background:#00000080;z-index:1299}::ng-deep .conversation-info-dialog .dialog-content{max-height:70vh;overflow-y:auto}::ng-deep .conversation-info-dialog .info-list{display:grid;grid-template-columns:auto 1fr;gap:8px;margin-bottom:20px}::ng-deep .conversation-info-dialog .info-list dt{font-weight:700}::ng-deep .conversation-info-dialog .message-history-list{max-height:300px;overflow-y:auto;border:1px solid #eee;border-radius:5px;padding:10px}::ng-deep .conversation-info-dialog .message-history-list .message-history-item{padding:5px 0;border-bottom:1px solid #f0f0f0}::ng-deep .conversation-info-dialog .message-history-list .message-history-item:last-child{border-bottom:none}::ng-deep .conversation-info-dialog .message-history-list .message-history-item .message-role{font-weight:700;margin-right:5px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i4.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i6.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: "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", "score", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }] }); }
|
|
1872
2706
|
}
|
|
1873
2707
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
1874
2708
|
type: Component,
|
|
1875
|
-
args: [{ selector: 'dc-chat', standalone: true, imports: [
|
|
2709
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [
|
|
2710
|
+
CommonModule,
|
|
2711
|
+
FormsModule,
|
|
2712
|
+
ChatMessageComponent,
|
|
2713
|
+
ReactiveFormsModule,
|
|
2714
|
+
SkeletonModule,
|
|
2715
|
+
DialogModule,
|
|
2716
|
+
ProgressBarModule,
|
|
2717
|
+
ChatHeaderComponent,
|
|
2718
|
+
ChatFooterComponent,
|
|
2719
|
+
], providers: [DialogService], template: "<dc-chat-header\n [isAdmin]=\"isAdmin\"\n [alternativeConversation]=\"alternativeConversation\"\n (restartConversationEvent)=\"restartConversation($event || null)\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\">\n</dc-chat-header>\n\n<main class=\"chat-container\" (touchmove)=\"$event.stopPropagation()\">\n <div class=\"chat\" aria-live=\"polite\">\n @for (message of messages; track message) { @switch (message.role) { @case ('assistant') {\n <div class=\"message left\">\n <img class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? imageUser\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('assistantHelper') {\n <div class=\"message-helper right\">\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('user') {\n <div class=\"message right\">\n <img class=\"logo fit-top-image\" [src]=\"user?.urlPicture || 'assets/defaults/avatar.jpg'\" [alt]=\"'User avatar'\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } } }\n\n <!-- AI thinking indicator -->\n @if (isAIThinking) {\n <div class=\"message message--assistant message--thinking\">\n <span class=\"thinking-emoji\" aria-hidden=\"true\">\uD83E\uDD14</span>\n <div class=\"thinking-indicator\">\n <div class=\"skeleton-container\">\n <p-skeleton width=\"100%\" />\n <br />\n <p-skeleton width=\"75%\" />\n </div>\n </div>\n </div>\n }\n\n <!-- User typing or transcribing indicator -->\n @if (isUserTalking || isGettingTranscription) {\n <div class=\"message message--user message--activity\">\n <img class=\"avatar avatar--user\" [src]=\"user?.urlPicture || imageUser\" [alt]=\"'User avatar'\" />\n @if (isUserTalking) {\n <p class=\"typing-indicator\" aria-label=\"User is speaking\">...</p>\n } @if (isGettingTranscription) {\n <i class=\"pi pi-spin pi-spinner-dotted\" aria-label=\"Transcribing speech\"></i>\n }\n </div>\n }\n </div>\n</main>\n\n<dc-chat-footer\n [isAIThinking]=\"isAIThinking\"\n [score]=\"score\"\n [micSettings]=\"micSettings\"\n (sendMessage)=\"sendUserMessage($event)\"\n (textInputChanged)=\"setInputText($event)\"\n (micFinishedEvent)=\"micFinished($event)\">\n</dc-chat-footer>\n\n<!-- Information dialog -->\n<p-dialog\n header=\"Informaci\u00F3n de la conversaci\u00F3n\"\n [(visible)]=\"isInfoVisible\"\n [modal]=\"true\"\n [responsive]=\"true\"\n styleClass=\"conversation-info-dialog\"\n aria-labelledby=\"conversation-info-title\">\n <div class=\"dialog-content\">\n <section class=\"model-info\">\n <h3 id=\"conversation-info-title\" class=\"sr-only\">Informaci\u00F3n de la conversaci\u00F3n</h3>\n <dl class=\"info-list\">\n <dt>Modelo:</dt>\n <dd>{{ chatUserSettings?.model?.provider }} - {{ chatUserSettings?.model?.modelName }}</dd>\n\n <dt>Tipo:</dt>\n <dd>{{ agentCard?.conversationSettings?.conversationType }}</dd>\n\n <dt>Text Engine:</dt>\n <dd>{{ agentCard?.conversationSettings?.textEngine }}</dd>\n\n <dt>Language:</dt>\n <dd>{{ agentCard?.lang }}</dd>\n\n <dt>TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.voice }}</dd>\n\n <dt>Secondary TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.secondaryVoice }}</dd>\n </dl>\n\n <div class=\"parameters\">\n <h4>Par\u00E1metros:</h4>\n <pre>{{ parseDict | json }}</pre>\n </div>\n </section>\n\n <section class=\"conversation-history\">\n <h4>Conversaci\u00F3n hasta ahora</h4>\n <div class=\"message-history-list\">\n @for (message of conversationSettings.messages; track message) {\n <div class=\"message-history-item\">\n <span class=\"message-role\">{{ message.role }}:</span>\n <span class=\"message-content\">{{ message.content }}</span>\n </div>\n }\n </div>\n </section>\n </div>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%}*{box-sizing:border-box}.chat-container{border-radius:10px;overflow:hidden;padding:15px;position:relative;max-width:100%;height:100%;display:flex;flex-direction:column}.chat{display:flex;flex-direction:column;list-style-type:none;padding:0;margin:0;overflow-y:auto;flex:1}.logo{width:30px;height:30px}.logo.fit-top-image{object-fit:cover;object-position:top;border-radius:50%}.message{background-color:#f6f6f6f2;border-radius:25px;box-shadow:0 5px 15px #0000001a;position:relative;margin-bottom:20px;transition:all .2s ease}.message .logo{position:absolute;top:50%;transform:translateY(-50%);border-radius:50%;cursor:pointer}.message .logo:hover{transform:translateY(-50%) scale(1.05)}.message.left{padding:12px 15px 12px 45px;margin-right:20%}.message.left .logo{left:5px}.message.right{align-self:flex-end;padding:12px 45px 12px 15px;margin-left:20%;background-color:#e6f0fff2}.message.right .logo{right:5px}.message--thinking,.message--activity{opacity:.7}.message-helper{background-color:#ffffffe6;box-shadow:0 3px 10px #0000001a;position:relative;margin-bottom:20px;border-radius:20px}.message-helper.right{align-self:flex-end;padding:8px 15px;margin-left:20%}.thinking-indicator{padding:5px}.thinking-indicator .skeleton-container{width:200px}::ng-deep .cdk-overlay-container,::ng-deep .cdk-global-overlay-wrapper{z-index:1400}::ng-deep .dialog-backdrop{background:#00000080;z-index:1299}::ng-deep .conversation-info-dialog .dialog-content{max-height:70vh;overflow-y:auto}::ng-deep .conversation-info-dialog .info-list{display:grid;grid-template-columns:auto 1fr;gap:8px;margin-bottom:20px}::ng-deep .conversation-info-dialog .info-list dt{font-weight:700}::ng-deep .conversation-info-dialog .message-history-list{max-height:300px;overflow-y:auto;border:1px solid #eee;border-radius:5px;padding:10px}::ng-deep .conversation-info-dialog .message-history-list .message-history-item{padding:5px 0;border-bottom:1px solid #f0f0f0}::ng-deep .conversation-info-dialog .message-history-list .message-history-item:last-child{border-bottom:none}::ng-deep .conversation-info-dialog .message-history-list .message-history-item .message-role{font-weight:700;margin-right:5px}\n"] }]
|
|
1876
2720
|
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
1877
2721
|
type: Inject,
|
|
1878
2722
|
args: [CONVERSATION_AI_TOKEN]
|
|
1879
|
-
}] }, { type: i6$
|
|
2723
|
+
}] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
1880
2724
|
type: Inject,
|
|
1881
2725
|
args: [TOAST_ALERTS_TOKEN]
|
|
1882
|
-
}] }, { type: DCConversationPromptBuilderService }, { type: i1$
|
|
2726
|
+
}] }, { type: DCConversationPromptBuilderService }, { type: i1$3.DialogService }, { type: i0.ChangeDetectorRef }], propDecorators: { chatUserSettings: [{
|
|
1883
2727
|
type: Input
|
|
1884
2728
|
}], agentCard: [{
|
|
1885
2729
|
type: Input
|
|
@@ -2030,7 +2874,7 @@ class PromptPreviewDialogComponent {
|
|
|
2030
2874
|
close() {
|
|
2031
2875
|
this.dialogRef.close();
|
|
2032
2876
|
}
|
|
2033
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, deps: [{ token: i1$
|
|
2877
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, deps: [{ token: i1$3.DynamicDialogConfig }, { token: i1$3.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2034
2878
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: PromptPreviewDialogComponent, isStandalone: true, selector: "dc-prompt-preview-dialog", ngImport: i0, template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DialogModule }] }); }
|
|
2035
2879
|
}
|
|
2036
2880
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, decorators: [{
|
|
@@ -2041,7 +2885,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2041
2885
|
imports: [CommonModule, DialogModule],
|
|
2042
2886
|
template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `,
|
|
2043
2887
|
}]
|
|
2044
|
-
}], ctorParameters: () => [{ type: i1$
|
|
2888
|
+
}], ctorParameters: () => [{ type: i1$3.DynamicDialogConfig }, { type: i1$3.DynamicDialogRef }] });
|
|
2045
2889
|
|
|
2046
2890
|
class TranslateDialogComponent {
|
|
2047
2891
|
constructor(fb, dialogRef, data) {
|
|
@@ -2063,7 +2907,7 @@ class TranslateDialogComponent {
|
|
|
2063
2907
|
onConfirm() {
|
|
2064
2908
|
this.dialogRef.close(this.form.value.targetLang);
|
|
2065
2909
|
}
|
|
2066
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, deps: [{ token: i1
|
|
2910
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, deps: [{ token: i1.FormBuilder }, { token: i2$1.DialogRef }, { token: DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2067
2911
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TranslateDialogComponent, isStandalone: true, selector: "dc-translate-dialog", ngImport: i0, template: `
|
|
2068
2912
|
<div class="translate-dialog">
|
|
2069
2913
|
<h4>Tu idioma actual es: {{ data.currentLang }}</h4>
|
|
@@ -2081,7 +2925,7 @@ class TranslateDialogComponent {
|
|
|
2081
2925
|
<button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
|
|
2082
2926
|
</div>
|
|
2083
2927
|
</div>
|
|
2084
|
-
`, isInline: true, styles: [".translate-dialog{padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 8px #00000026}.actions{margin-top:20px;display:flex;justify-content:flex-end;gap:10px}select{width:100%;padding:8px;margin-top:10px;border:1px solid #ccc;border-radius:4px}button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer}button:first-child{background:#f0f0f0}button:last-child{background:#007bff;color:#fff}button:disabled{background:#ccc;cursor:not-allowed}h2{margin:0 0 16px;color:#333}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type:
|
|
2928
|
+
`, isInline: true, styles: [".translate-dialog{padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 8px #00000026}.actions{margin-top:20px;display:flex;justify-content:flex-end;gap:10px}select{width:100%;padding:8px;margin-top:10px;border:1px solid #ccc;border-radius:4px}button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer}button:first-child{background:#f0f0f0}button:last-child{background:#007bff;color:#fff}button:disabled{background:#ccc;cursor:not-allowed}h2{margin:0 0 16px;color:#333}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
|
|
2085
2929
|
}
|
|
2086
2930
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, decorators: [{
|
|
2087
2931
|
type: Component,
|
|
@@ -2103,11 +2947,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2103
2947
|
</div>
|
|
2104
2948
|
</div>
|
|
2105
2949
|
`, styles: [".translate-dialog{padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 8px #00000026}.actions{margin-top:20px;display:flex;justify-content:flex-end;gap:10px}select{width:100%;padding:8px;margin-top:10px;border:1px solid #ccc;border-radius:4px}button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer}button:first-child{background:#f0f0f0}button:last-child{background:#007bff;color:#fff}button:disabled{background:#ccc;cursor:not-allowed}h2{margin:0 0 16px;color:#333}\n"] }]
|
|
2106
|
-
}], ctorParameters: () => [{ type: i1
|
|
2950
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$1.DialogRef }, { type: undefined, decorators: [{
|
|
2107
2951
|
type: Inject,
|
|
2108
2952
|
args: [DIALOG_DATA]
|
|
2109
2953
|
}] }] });
|
|
2110
2954
|
|
|
2955
|
+
function markdownToHTML(markdownText) {
|
|
2956
|
+
// Convert italics-bold (***text***)
|
|
2957
|
+
let htmlText = markdownText.replace(/\*\*\*(.+?)\*\*\*/g, '<em><strong>$1</strong></em>');
|
|
2958
|
+
// Convert bold (**text**)
|
|
2959
|
+
htmlText = htmlText.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
|
2960
|
+
// Convert italics (*text*)
|
|
2961
|
+
htmlText = htmlText.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
|
2962
|
+
// Convert text enclosed by double quotes ("text")
|
|
2963
|
+
// htmlText = htmlText.replace(/"(.+?)"/g, '<cite>$1</cite>');
|
|
2964
|
+
return htmlText;
|
|
2965
|
+
}
|
|
2966
|
+
class SimpleMdToHtmlPipe {
|
|
2967
|
+
transform(text) {
|
|
2968
|
+
return markdownToHTML(text);
|
|
2969
|
+
}
|
|
2970
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SimpleMdToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2971
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: SimpleMdToHtmlPipe, isStandalone: true, name: "simpleMdToHtml" }); }
|
|
2972
|
+
}
|
|
2973
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SimpleMdToHtmlPipe, decorators: [{
|
|
2974
|
+
type: Pipe,
|
|
2975
|
+
args: [{
|
|
2976
|
+
name: 'simpleMdToHtml',
|
|
2977
|
+
standalone: true,
|
|
2978
|
+
}]
|
|
2979
|
+
}] });
|
|
2980
|
+
class StartDivToHtmlPipe {
|
|
2981
|
+
// solo create espacio entre lineas
|
|
2982
|
+
transform(text) {
|
|
2983
|
+
text = text.replace(/<start>/gi, '<hr>');
|
|
2984
|
+
text = text.replace(/\n/g, '<br>');
|
|
2985
|
+
return text;
|
|
2986
|
+
}
|
|
2987
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: StartDivToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2988
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: StartDivToHtmlPipe, isStandalone: true, name: "startDividerToHtml" }); }
|
|
2989
|
+
}
|
|
2990
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: StartDivToHtmlPipe, decorators: [{
|
|
2991
|
+
type: Pipe,
|
|
2992
|
+
args: [{
|
|
2993
|
+
name: 'startDividerToHtml',
|
|
2994
|
+
standalone: true,
|
|
2995
|
+
}]
|
|
2996
|
+
}] });
|
|
2997
|
+
class MdToHtmlArrayPipe {
|
|
2998
|
+
// devuelve un array de objetos con el contenido y el tag
|
|
2999
|
+
transform(text) {
|
|
3000
|
+
const htmlArray = convertToHTML(text);
|
|
3001
|
+
return htmlArray.map((val) => val.content).join('');
|
|
3002
|
+
}
|
|
3003
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
3004
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, isStandalone: true, name: "mdToHtmlArray" }); }
|
|
3005
|
+
}
|
|
3006
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, decorators: [{
|
|
3007
|
+
type: Pipe,
|
|
3008
|
+
args: [{
|
|
3009
|
+
name: 'mdToHtmlArray',
|
|
3010
|
+
standalone: true,
|
|
3011
|
+
}]
|
|
3012
|
+
}] });
|
|
3013
|
+
|
|
2111
3014
|
class AccountPlatformForm {
|
|
2112
3015
|
constructor(route, fb, router, toastService) {
|
|
2113
3016
|
this.route = route;
|
|
@@ -2168,13 +3071,13 @@ class AccountPlatformForm {
|
|
|
2168
3071
|
this.toastService.warn(infoToast);
|
|
2169
3072
|
}
|
|
2170
3073
|
}
|
|
2171
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, deps: [{ token: i1$
|
|
2172
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1
|
|
3074
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, deps: [{ token: i1$4.ActivatedRoute }, { token: i1.FormBuilder }, { token: i1$4.Router }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3075
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i4$3.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2173
3076
|
}
|
|
2174
3077
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, decorators: [{
|
|
2175
3078
|
type: Component,
|
|
2176
3079
|
args: [{ selector: 'account-platform-form', imports: [ReactiveFormsModule, CardModule, TextareaModule, DropdownModule, ButtonModule, SelectModule, InputTextModule, ChipModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"] }]
|
|
2177
|
-
}], ctorParameters: () => [{ type: i1$
|
|
3080
|
+
}], ctorParameters: () => [{ type: i1$4.ActivatedRoute }, { type: i1.FormBuilder }, { type: i1$4.Router }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
2178
3081
|
type: Inject,
|
|
2179
3082
|
args: [TOAST_ALERTS_TOKEN]
|
|
2180
3083
|
}] }], propDecorators: { formArray: [{
|
|
@@ -2208,6 +3111,7 @@ class DCAgentCardFormComponent {
|
|
|
2208
3111
|
this.accountsOptions = Object.values(EAccountsPlatform);
|
|
2209
3112
|
this.languageOptions = Object.entries(LangCodeDescriptionEs).map(([value, label]) => ({ value, label }));
|
|
2210
3113
|
this.agentCardId = this.activatedRoute.snapshot.paramMap.get('id');
|
|
3114
|
+
this.audioSpeedOptions = Object.values(AudioSpeed$1).map((speed) => ({ label: speed, value: speed }));
|
|
2211
3115
|
this.storageSettings = this.getSettings();
|
|
2212
3116
|
this.bannerImgSettings = {
|
|
2213
3117
|
path: 'conversation-cards/' + this.agentCardId,
|
|
@@ -2252,9 +3156,9 @@ class DCAgentCardFormComponent {
|
|
|
2252
3156
|
textEngine: [TextEngines.SimpleText],
|
|
2253
3157
|
conversationType: [ConversationType.General],
|
|
2254
3158
|
autoStart: [true],
|
|
3159
|
+
tts: this.fb.group({ voice: [''], secondaryVoice: [''], speed: ['1.0'], speedRate: [1.0] }),
|
|
2255
3160
|
}),
|
|
2256
3161
|
lang: [''],
|
|
2257
|
-
tts: this.fb.group({ voice: [''], secondaryVoice: [''], speed: ['1.0'], speedRate: [1.0] }),
|
|
2258
3162
|
metaApp: this.fb.group({ isPublished: [false], isPublic: [false], authorId: [''], authorEmail: [''], takenCount: [0] }),
|
|
2259
3163
|
model: this.fb.group({ id: [''], modelName: [''], provider: [''] }),
|
|
2260
3164
|
accounts: this.fb.array([]),
|
|
@@ -2597,12 +3501,12 @@ class DCAgentCardFormComponent {
|
|
|
2597
3501
|
this.toastService.success({ title: 'Sticker removed', subtitle: 'Sticker was removed' });
|
|
2598
3502
|
this.cdr.detectChanges();
|
|
2599
3503
|
}
|
|
2600
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, deps: [{ token: i1
|
|
2601
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-conversation-form", inputs: { storageSettings: "storageSettings", bannerImgSettings: "bannerImgSettings", imageStorageSettings: "imageStorageSettings" }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, 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 </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 <input pInputText id=\"speed\" type=\"text\" formControlName=\"speed\" />\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\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 <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\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 <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\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 <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\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 <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 <div *ngFor=\"let tag of form.controls.characterCard.controls.data.controls.tags.controls; let i = index\" 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 <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"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i7$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i7$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: TextareaModule }, { kind: "directive", type: i8$1.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$1.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToggleButtonModule }, { kind: "component", type: i11.ToggleButton, selector: "p-toggleButton, p-togglebutton, p-toggle-button", inputs: ["onLabel", "offLabel", "onIcon", "offIcon", "ariaLabel", "ariaLabelledBy", "disabled", "style", "styleClass", "inputId", "tabindex", "size", "iconPos", "autofocus", "allowEmpty"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i13.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["style", "styleClass", "tabindex", "inputId", "name", "disabled", "readonly", "trueValue", "falseValue", "ariaLabel", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: MdToHtmlArrayPipe, name: "mdToHtmlArray" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i14.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i15.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }, { kind: "component", type: AccountPlatformForm, selector: "account-platform-form", inputs: ["formArray"] }] }); }
|
|
3504
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.MultiImagesStorageService }, { token: CONVERSATION_AI_TOKEN }, { token: i0.ChangeDetectorRef }, { token: i1$4.Router }, { token: i1$4.ActivatedRoute }, { token: i1$3.DialogService }, { token: DCConversationPromptBuilderService }, { token: TOAST_ALERTS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3505
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: "storageSettings", bannerImgSettings: "bannerImgSettings", imageStorageSettings: "imageStorageSettings" }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, 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 <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\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 <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\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 <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\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 <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 <div *ngFor=\"let tag of form.controls.characterCard.controls.data.controls.tags.controls; let i = index\" 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 <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"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i7.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i7.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: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToggleButtonModule }, { kind: "component", type: i11.ToggleButton, selector: "p-toggleButton, p-togglebutton, p-toggle-button", inputs: ["onLabel", "offLabel", "onIcon", "offIcon", "ariaLabel", "ariaLabelledBy", "disabled", "style", "styleClass", "inputId", "tabindex", "size", "iconPos", "autofocus", "allowEmpty"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i13.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["style", "styleClass", "tabindex", "inputId", "name", "disabled", "readonly", "trueValue", "falseValue", "ariaLabel", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: MdToHtmlArrayPipe, name: "mdToHtmlArray" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i14.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i15.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }, { kind: "component", type: AccountPlatformForm, selector: "account-platform-form", inputs: ["formArray"] }] }); }
|
|
2602
3506
|
}
|
|
2603
3507
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
|
|
2604
3508
|
type: Component,
|
|
2605
|
-
args: [{ selector: 'dc-
|
|
3509
|
+
args: [{ selector: 'dc-agent-form', standalone: true, providers: [DialogService], imports: [
|
|
2606
3510
|
CommonModule,
|
|
2607
3511
|
ReactiveFormsModule,
|
|
2608
3512
|
CropperComponentModal,
|
|
@@ -2623,11 +3527,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2623
3527
|
PopoverModule,
|
|
2624
3528
|
ProviderSelectorComponent,
|
|
2625
3529
|
AccountPlatformForm,
|
|
2626
|
-
], 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 </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 <input pInputText id=\"speed\" type=\"text\" formControlName=\"speed\" />\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\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 <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\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 <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\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 <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\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 <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 <div *ngFor=\"let tag of form.controls.characterCard.controls.data.controls.tags.controls; let i = index\" 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 <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"] }]
|
|
2627
|
-
}], ctorParameters: () => [{ type: i1
|
|
3530
|
+
], 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 <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\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 <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\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 <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\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 <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\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 <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 <div *ngFor=\"let tag of form.controls.characterCard.controls.data.controls.tags.controls; let i = index\" 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 <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"] }]
|
|
3531
|
+
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.MultiImagesStorageService }, { type: AgentCardsAbstractService, decorators: [{
|
|
2628
3532
|
type: Inject,
|
|
2629
3533
|
args: [CONVERSATION_AI_TOKEN]
|
|
2630
|
-
}] }, { type: i0.ChangeDetectorRef }, { type: i1$
|
|
3534
|
+
}] }, { type: i0.ChangeDetectorRef }, { type: i1$4.Router }, { type: i1$4.ActivatedRoute }, { type: i1$3.DialogService }, { type: DCConversationPromptBuilderService }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
2631
3535
|
type: Optional
|
|
2632
3536
|
}, {
|
|
2633
3537
|
type: Inject,
|
|
@@ -2655,6 +3559,7 @@ class DCConversationCardUIComponent {
|
|
|
2655
3559
|
{ label: 'Delete', icon: 'pi pi-trash', title: 'delete', severity: 'danger', command: () => this.onDelete() },
|
|
2656
3560
|
{ label: 'Select', icon: 'pi pi-check', title: 'select', severity: 'success', command: () => this.onDetails() },
|
|
2657
3561
|
];
|
|
3562
|
+
this.showOptions = true;
|
|
2658
3563
|
this.onCardAction = new EventEmitter();
|
|
2659
3564
|
}
|
|
2660
3565
|
ngOnInit() {
|
|
@@ -2672,13 +3577,15 @@ class DCConversationCardUIComponent {
|
|
|
2672
3577
|
this.onCardAction.emit({ event: 'delete', card: this.card });
|
|
2673
3578
|
}
|
|
2674
3579
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2675
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
3580
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: "card", showOptions: "showOptions" }, outputs: { onCardAction: "onCardAction" }, ngImport: i0, template: "<p-card class=\"card-image\">\n @if(showOptions) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card.title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card.characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: PopoverModule }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.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$3.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: i3$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2676
3581
|
}
|
|
2677
3582
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
|
|
2678
3583
|
type: Component,
|
|
2679
|
-
args: [{ selector: 'dc-agent-card-default-ui', imports: [PopoverModule, TruncatePipe, ButtonModule, SpeedDialModule, CardModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p-card class=\"card-image\">\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 <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card.title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card.characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"] }]
|
|
3584
|
+
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 <h2 class=\"title-text\">{{ card.title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card.characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"] }]
|
|
2680
3585
|
}], propDecorators: { card: [{
|
|
2681
3586
|
type: Input
|
|
3587
|
+
}], showOptions: [{
|
|
3588
|
+
type: Input
|
|
2682
3589
|
}], onCardAction: [{
|
|
2683
3590
|
type: Output
|
|
2684
3591
|
}] } });
|
|
@@ -2698,6 +3605,7 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
2698
3605
|
this.toastService = toastService;
|
|
2699
3606
|
this.cdr = cdr;
|
|
2700
3607
|
this.viewMode = 'cards';
|
|
3608
|
+
this.showOptions = true;
|
|
2701
3609
|
this.gridLayout = true;
|
|
2702
3610
|
this.agentCards = [];
|
|
2703
3611
|
this.cardEventSubs = [];
|
|
@@ -2814,8 +3722,8 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
2814
3722
|
this.doAction(actionEvent); // handle by father.
|
|
2815
3723
|
}
|
|
2816
3724
|
}
|
|
2817
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AgentCardListComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: i1$
|
|
2818
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: "viewMode", customCardComponent: "customCardComponent", gridLayout: "gridLayout", getCustomButtons: "getCustomButtons" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (
|
|
3725
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AgentCardListComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: i1$4.ActivatedRoute }, { token: i1$4.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3726
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: "viewMode", customCardComponent: "customCardComponent", showOptions: "showOptions", gridLayout: "gridLayout", getCustomButtons: "getCustomButtons" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.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: i3$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onFilterAction", "onChangeSort"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i4.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
|
|
2819
3727
|
}
|
|
2820
3728
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AgentCardListComponent, decorators: [{
|
|
2821
3729
|
type: Component,
|
|
@@ -2829,17 +3737,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2829
3737
|
SkeletonModule,
|
|
2830
3738
|
SpeedDialModule,
|
|
2831
3739
|
QuickTableComponent,
|
|
2832
|
-
], standalone: true, template: "<dc-filter-bar [isAdmin]=\"true\" (
|
|
3740
|
+
], standalone: true, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"] }]
|
|
2833
3741
|
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
2834
3742
|
type: Inject,
|
|
2835
3743
|
args: [CONVERSATION_AI_TOKEN]
|
|
2836
|
-
}] }, { type: i6$
|
|
3744
|
+
}] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
2837
3745
|
type: Inject,
|
|
2838
3746
|
args: [TOAST_ALERTS_TOKEN]
|
|
2839
|
-
}] }, { type: i1$
|
|
3747
|
+
}] }, { type: i1$4.ActivatedRoute }, { type: i1$4.Router }, { type: i0.ChangeDetectorRef }], propDecorators: { viewMode: [{
|
|
2840
3748
|
type: Input
|
|
2841
3749
|
}], customCardComponent: [{
|
|
2842
3750
|
type: Input
|
|
3751
|
+
}], showOptions: [{
|
|
3752
|
+
type: Input
|
|
2843
3753
|
}], gridLayout: [{
|
|
2844
3754
|
type: Input
|
|
2845
3755
|
}], getCustomButtons: [{
|
|
@@ -2849,50 +3759,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2849
3759
|
args: ['outlet']
|
|
2850
3760
|
}] } });
|
|
2851
3761
|
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
3762
|
+
class ParseCardPipe {
|
|
3763
|
+
constructor() {
|
|
3764
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
3765
|
+
this.builderConversation = inject(DCConversationPromptBuilderService);
|
|
3766
|
+
}
|
|
3767
|
+
transform(text, card = null) {
|
|
3768
|
+
let parseDict = this.userDataExchange.getParseDict();
|
|
3769
|
+
if (card) {
|
|
3770
|
+
const cardMacros = { char: card?.characterCard?.data?.name };
|
|
3771
|
+
parseDict = { ...parseDict, ...cardMacros };
|
|
3772
|
+
}
|
|
3773
|
+
const result = this.builderConversation.applyReplacements(text, parseDict);
|
|
3774
|
+
return result;
|
|
3775
|
+
}
|
|
3776
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
3777
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, isStandalone: true, name: "parseCard" }); }
|
|
3778
|
+
}
|
|
3779
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, decorators: [{
|
|
3780
|
+
type: Pipe,
|
|
3781
|
+
args: [{
|
|
3782
|
+
name: 'parseCard',
|
|
3783
|
+
standalone: true,
|
|
3784
|
+
}]
|
|
3785
|
+
}] });
|
|
3786
|
+
|
|
2859
3787
|
class DcAgentCardDetailsComponent {
|
|
2860
|
-
constructor(agentCardService, route, cdr) {
|
|
3788
|
+
constructor(agentCardService, route, cdr, userDataExchange) {
|
|
2861
3789
|
this.agentCardService = agentCardService;
|
|
2862
3790
|
this.route = route;
|
|
2863
3791
|
this.cdr = cdr;
|
|
2864
|
-
this.
|
|
3792
|
+
this.userDataExchange = userDataExchange;
|
|
3793
|
+
this.agentCardId = '';
|
|
2865
3794
|
this.onStartConversation = new EventEmitter();
|
|
3795
|
+
this.showInfoLayer = false;
|
|
2866
3796
|
}
|
|
2867
3797
|
async ngOnInit() {
|
|
2868
|
-
console.log('DcAgentCardDetailsComponent', this.
|
|
3798
|
+
console.log('DcAgentCardDetailsComponent', this.agentCardId);
|
|
2869
3799
|
const id = this.route.snapshot.paramMap.get('id');
|
|
2870
3800
|
if (id) {
|
|
2871
|
-
this.
|
|
2872
|
-
console.log(this.
|
|
3801
|
+
this.agentCardId = id;
|
|
3802
|
+
console.log(this.agentCardId);
|
|
2873
3803
|
}
|
|
2874
|
-
this.
|
|
2875
|
-
if (!this.
|
|
3804
|
+
this.agentCard = await this.agentCardService.findConversationCardByID(this.agentCardId);
|
|
3805
|
+
if (!this.agentCard.conversationSettings) {
|
|
2876
3806
|
console.warn('⚠️ Conversation settings not found ⚠️ probably is an old version of the card.');
|
|
2877
|
-
this.
|
|
3807
|
+
this.agentCard.conversationSettings = {};
|
|
2878
3808
|
}
|
|
2879
|
-
console.log(this.
|
|
3809
|
+
console.log(this.agentCard);
|
|
2880
3810
|
this.cdr.detectChanges();
|
|
2881
3811
|
}
|
|
2882
3812
|
startConversation() {
|
|
2883
|
-
console.log('⚠️ last version startConversation', this.
|
|
2884
|
-
this.onStartConversation.emit(this.
|
|
3813
|
+
console.log('⚠️ last version startConversation', this.agentCard);
|
|
3814
|
+
this.onStartConversation.emit(this.agentCard);
|
|
3815
|
+
}
|
|
3816
|
+
toggleInfoLayer() {
|
|
3817
|
+
this.showInfoLayer = !this.showInfoLayer;
|
|
3818
|
+
this.cdr.markForCheck();
|
|
2885
3819
|
}
|
|
2886
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: i1$
|
|
2887
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: {
|
|
3820
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: i1$4.ActivatedRoute }, { token: i0.ChangeDetectorRef }, { token: USER_DATA_EXCHANGE }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3821
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", 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 <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n\n<!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.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: i3$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
|
|
2888
3822
|
}
|
|
2889
3823
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
|
|
2890
3824
|
type: Component,
|
|
2891
|
-
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [CommonModule, ButtonModule], template: "<div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{
|
|
3825
|
+
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [CommonModule, 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 <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n\n<!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"] }]
|
|
2892
3826
|
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
2893
3827
|
type: Inject,
|
|
2894
3828
|
args: [CONVERSATION_AI_TOKEN]
|
|
2895
|
-
}] }, { type: i1$
|
|
3829
|
+
}] }, { type: i1$4.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: UserDataExchangeAbstractService, decorators: [{
|
|
3830
|
+
type: Inject,
|
|
3831
|
+
args: [USER_DATA_EXCHANGE]
|
|
3832
|
+
}] }], propDecorators: { agentCardId: [{
|
|
2896
3833
|
type: Input
|
|
2897
3834
|
}], onStartConversation: [{
|
|
2898
3835
|
type: Output
|
|
@@ -2907,5 +3844,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2907
3844
|
* Generated bundle index. Do not edit.
|
|
2908
3845
|
*/
|
|
2909
3846
|
|
|
2910
|
-
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN,
|
|
3847
|
+
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatContainerComponent, ChatMessage, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, ProviderSelectorComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, characterCardStringDataDefinition, defaultconvUserSettings, extractJsonFromResponse, provideChatAIService, provideUserDataExchange };
|
|
2911
3848
|
//# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map
|