@dataclouder/ngx-agent-cards 0.0.83 → 0.0.85

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.
@@ -1,42 +1,44 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, Inject, Component, Input, Pipe, ChangeDetectionStrategy, EventEmitter, Output, ViewChild, Optional, ViewChildren } from '@angular/core';
3
- import * as i2 from '@angular/common';
2
+ import { InjectionToken, Injectable, Inject, Component, Input, signal, inject, DestroyRef, EventEmitter, effect, ChangeDetectionStrategy, Output, Pipe, ViewChild, Optional, ViewChildren } from '@angular/core';
3
+ import * as i1$1 from '@angular/common';
4
4
  import { CommonModule, NgComponentOutlet } from '@angular/common';
5
5
  import * as i1 from '@angular/platform-browser';
6
- import * as i1$1 from '@angular/forms';
7
- import { ReactiveFormsModule, FormControl, FormsModule } from '@angular/forms';
8
- import * as i1$2 from 'primeng/dynamicdialog';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+ import { BehaviorSubject, Subject, fromEvent, filter } from 'rxjs';
8
+ import { takeUntil, map } from 'rxjs/operators';
9
+ import * as i1$2 from '@angular/forms';
10
+ import { FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
11
+ import * as i1$3 from 'primeng/dynamicdialog';
9
12
  import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog';
10
- import * as i6 from 'primeng/skeleton';
13
+ import * as i4$1 from 'primeng/skeleton';
11
14
  import { SkeletonModule } from 'primeng/skeleton';
12
- import * as i6$1 from 'primeng/dialog';
15
+ import * as i5$1 from 'primeng/dialog';
13
16
  import { DialogModule } from 'primeng/dialog';
14
- import * as i9 from '@dataclouder/ngx-core';
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';
17
+ import * as i2 from 'primeng/progressbar';
18
18
  import { ProgressBarModule } from 'primeng/progressbar';
19
- import * as i4$1 from 'primeng/checkbox';
19
+ import { DCMicComponent } from '@dataclouder/ngx-mic';
20
+ import * as i3 from 'primeng/textarea';
21
+ import { TextareaModule } from 'primeng/textarea';
22
+ import * as i7 from 'primeng/button';
23
+ import { ButtonModule } from 'primeng/button';
24
+ import * as i6 from '@dataclouder/ngx-core';
25
+ import { TOAST_ALERTS_TOKEN, AudioSpeed as AudioSpeed$1, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
26
+ import * as i4$2 from 'primeng/checkbox';
20
27
  import { CheckboxModule } from 'primeng/checkbox';
21
28
  import { SliderModule } from 'primeng/slider';
22
- import * as i3 from 'primeng/radiobutton';
29
+ import * as i3$1 from 'primeng/radiobutton';
23
30
  import { RadioButtonModule } from 'primeng/radiobutton';
24
- import * as i7$1 from 'primeng/button';
25
- import { ButtonModule } from 'primeng/button';
26
31
  import * as i8 from 'primeng/rating';
27
32
  import { RatingModule } from 'primeng/rating';
28
33
  import * as i5 from 'primeng/table';
29
34
  import { TableModule } from 'primeng/table';
30
35
  import { BadgeModule } from 'primeng/badge';
31
- import * as i7 from 'primeng/tooltip';
36
+ import * as i7$1 from 'primeng/tooltip';
32
37
  import { TooltipModule } from 'primeng/tooltip';
33
- import { filter } from 'rxjs';
34
38
  import * as i4 from 'primeng/api';
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
- import * as i5$1 from 'primeng/inputtext';
41
+ import * as i5$2 from 'primeng/inputtext';
40
42
  import { InputTextModule } from 'primeng/inputtext';
41
43
  import * as i11 from 'primeng/togglebutton';
42
44
  import { ToggleButtonModule } from 'primeng/togglebutton';
@@ -50,15 +52,15 @@ 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$1 from 'primeng/card';
55
+ import * as i3$2 from 'primeng/card';
54
56
  import { CardModule } from 'primeng/card';
55
- import * as i4$2 from 'primeng/dropdown';
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$3 from '@angular/router';
59
- import * as i3$2 from 'primeng/paginator';
60
+ import * as i1$4 from '@angular/router';
61
+ import * as i3$3 from 'primeng/paginator';
60
62
  import { PaginatorModule } from 'primeng/paginator';
61
- import * as i5$2 from 'primeng/speeddial';
63
+ import * as i2$3 from 'primeng/speeddial';
62
64
  import { SpeedDialModule } from 'primeng/speeddial';
63
65
 
64
66
  const characterCardStringDataDefinition = `
@@ -79,6 +81,9 @@ 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
88
  class ChatMultiMessage {
84
89
  }
@@ -119,6 +124,20 @@ const LangCodeDescriptionEs = {
119
124
  pt: 'Portugués',
120
125
  fr: 'Frances',
121
126
  };
127
+ // TODO: use the default settings when the user is not set
128
+ const defaultconvUserSettings = {
129
+ realTime: false,
130
+ repeatRecording: false,
131
+ fixGrammar: false,
132
+ superHearing: false,
133
+ voice: '',
134
+ autoTranslate: false,
135
+ highlightWords: true,
136
+ synthVoice: true,
137
+ model: { modelName: null, provider: 'google' },
138
+ speed: null,
139
+ speedRate: null, // temporal
140
+ };
122
141
  class VoiceTTSOption {
123
142
  }
124
143
  const VoiceTTSOptions = [
@@ -519,7 +538,7 @@ class DCConversationPromptBuilderService {
519
538
  }
520
539
  // For chat conversation i need inital settings.
521
540
  buildConversationSettings(agentCard, parseDict = null) {
522
- const converstionSettings = agentCard.conversationSettings;
541
+ const converstionSettings = agentCard?.conversationSettings;
523
542
  converstionSettings.messages = this.buildConversationMessages(agentCard, parseDict);
524
543
  converstionSettings.last_prompt = this.getJailBrakePrompt(agentCard);
525
544
  return converstionSettings;
@@ -531,17 +550,25 @@ class DCConversationPromptBuilderService {
531
550
  this.parseConversation(initialConversation, parseDict);
532
551
  return initialConversation;
533
552
  }
534
- getDefaultParseDict(parseDict, conversation) {
553
+ getDefaultParseDict(parseDict, card) {
554
+ // Example of return { char: 'AI Bot', user: 'jordan', ... custom params, base: 'spanish' };
555
+ // conversation will parse values with curren value in parseDcit:
556
+ // Hello {{user}} im {{char}} your are talking {{base}} -> hello jordan im AI bot your are talking spanish
557
+ // important you have to implement userDataExchange to return what you want in parseDict that have sense for your app.
535
558
  if (parseDict) {
536
559
  return parseDict;
537
560
  }
538
- if (!parseDict) {
561
+ else {
539
562
  parseDict = this.userDataExchange.getParseDict();
540
- if (!parseDict) {
541
- parseDict = { char: conversation.characterCard.data.name || 'Bot' };
542
- }
563
+ const cardParseDict = this.getDefaultCardParseDict(card);
564
+ parseDict = { ...parseDict, ...cardParseDict };
565
+ return parseDict;
543
566
  }
544
- return parseDict;
567
+ }
568
+ getDefaultCardParseDict(card) {
569
+ // bot so far i don't use more than the character name.
570
+ // this is what silly tavern call macros https://docs.sillytavern.app/usage/core-concepts/macros/#general-macros
571
+ return { char: card.characterCard.data.name || 'Bot' };
545
572
  }
546
573
  convertConversationToHtml(messages, jailBrake = '') {
547
574
  let finalPrompt = '';
@@ -643,8 +670,8 @@ class DCConversationPromptBuilderService {
643
670
  last_prompt: jailBrakePrompt,
644
671
  textEngine: agent.conversationSettings.textEngine,
645
672
  conversationType: ConversationType.Scenario,
646
- voice: agent.tts.voice,
647
- secondaryVoice: agent.tts.secondaryVoice,
673
+ voice: agent.conversationSettings.tts.voice,
674
+ secondaryVoice: agent.conversationSettings.tts.secondaryVoice,
648
675
  // overrideConversationSettings: { autoTranslate: false, highlightWords: false, realTime: false, provider: 'google' },
649
676
  };
650
677
  return settings;
@@ -662,129 +689,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
662
689
  args: [USER_DATA_EXCHANGE]
663
690
  }] }] });
664
691
 
665
- const ICONS = {
666
- gear: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
667
- <path d="M12 1.75a1.25 1.25 0 0 1 1.25 1.25v.917a8.456 8.456 0 0 1 2.334.98l.645-.647a1.25 1.25 0 1 1 1.768 1.768l-.646.645c.389.69.729 1.437.98 2.334h.917a1.25 1.25 0 0 1 0 2.5h-.917a8.456 8.456 0 0 1-.98 2.334l.646.645a1.25 1.25 0 1 1-1.768 1.768l-.645-.646a8.456 8.456 0 0 1-2.334.98v.917a1.25 1.25 0 0 1-2.5 0v-.917a8.456 8.456 0 0 1-2.334-.98l-.645.646a1.25 1.25 0 1 1-1.768-1.768l.646-.645a8.456 8.456 0 0 1-.98-2.334H1.75a1.25 1.25 0 0 1 0-2.5h.917a8.456 8.456 0 0 1 .98-2.334l-.646-.645a1.25 1.25 0 1 1 1.768-1.768l.645.647c.69-.389 1.437-.729 2.334-.98V3a1.25 1.25 0 0 1 1.25-1.25h.083ZM12 7.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9Z"/>
668
- </svg>
669
- `,
670
- chat: `<svg viewBox="0 0 24 24" fill="currentColor">
671
- <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
672
- </svg>`,
673
- play: `<svg
674
- xmlns="http://www.w3.org/2000/svg"
675
- width="16"
676
- height="16"
677
- viewBox="0 0 24 24"
678
- fill="none"
679
- stroke="#263042"
680
- stroke-width="2"
681
- stroke-linecap="round"
682
- stroke-linejoin="round">
683
- <circle cx="12" cy="12" r="10"></circle>
684
- <polygon points="10 8 16 12 10 16 10 8"></polygon>
685
- </svg>`,
686
- loading: `<svg
687
- xmlns="http://www.w3.org/2000/svg"
688
- width="16"
689
- height="16"
690
- viewBox="0 0 24 24"
691
- fill="none"
692
- stroke="#263042"
693
- stroke-width="2"
694
- stroke-linecap="round"
695
- stroke-linejoin="round">
696
- <line x1="12" y1="2" x2="12" y2="6"></line>
697
- <line x1="12" y1="18" x2="12" y2="22"></line>
698
- <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
699
- <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
700
- <line x1="2" y1="12" x2="6" y2="12"></line>
701
- <line x1="18" y1="12" x2="22" y2="12"></line>
702
- <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
703
- <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
704
- </svg>`,
705
- user: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
706
- <!-- Head circle -->
707
- <circle cx="12" cy="8" r="4" fill="currentColor"/>
708
-
709
- <!-- Body shape -->
710
- <path d="M20 19v1a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-1c0-3.87 3.13-7 7-7h2c3.87 0 7 3.13 7 7z" fill="currentColor"/>
711
- </svg>`,
712
- ai: `<svg
713
- xmlns="http://www.w3.org/2000/svg"
714
- viewBox="0 0 100 100"
715
- width="100"
716
- height="100"
717
- fill="none"
718
- stroke="black"
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
- };
743
-
744
- class IconsComponent {
745
- constructor(sanitizer) {
746
- this.sanitizer = sanitizer;
747
- this.size = 14;
748
- this.color = 'currentColor';
749
- }
750
- ngOnChanges(changes) {
751
- if (changes['name']) {
752
- const svg = ICONS[this.name] || '';
753
- this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
754
- }
755
- }
756
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
757
- 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: `
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"] }); }
767
- }
768
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, decorators: [{
769
- type: Component,
770
- args: [{ selector: 'dc-icon', template: `
771
- <span
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: [{
781
- type: Input
782
- }], size: [{
783
- type: Input
784
- }], color: [{
785
- type: Input
786
- }] } });
787
-
788
692
  function markdownToHtml(markdown) {
789
693
  const htmlArray = [];
790
694
  const lines = markdown.split('\n');
@@ -968,23 +872,307 @@ function markdownToHTML2(markdown) {
968
872
  result.push({ content: `<q>${quoteText}</q>`, tag: 'q', text: quoteText });
969
873
  }
970
874
  }
971
- return result;
972
- }
973
- function removeEmojis(text) {
974
- // This regex pattern matches emoji characters
975
- return text.replace(/[\u{1F000}-\u{1F6FF}|\u{1F900}-\u{1F9FF}|\u{2600}-\u{26FF}|\u{2700}-\u{27BF}|\u{1F300}-\u{1F5FF}|\u{1F680}-\u{1F6FF}|\u{1F1E0}-\u{1F1FF}|\u{1F900}-\u{1F9FF}|\u{1F100}-\u{1F1FF}|\u{1F200}-\u{1F2FF}|\u{2100}-\u{26FF}]/gu, '');
976
- }
977
- function removeAllEmojis(text) {
978
- // More comprehensive regex that catches:
979
- // 1. Standard emojis
980
- // 2. Emoji variations and modifiers
981
- // 3. Symbol characters
982
- // 4. Pictographs
983
- // 5. Emoticons
984
- // 6. Regional indicators
985
- // 7. Flags
986
- 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, '');
875
+ return result;
876
+ }
877
+ function removeEmojis(text) {
878
+ // This regex pattern matches emoji characters
879
+ return text.replace(/[\u{1F000}-\u{1F6FF}|\u{1F900}-\u{1F9FF}|\u{2600}-\u{26FF}|\u{2700}-\u{27BF}|\u{1F300}-\u{1F5FF}|\u{1F680}-\u{1F6FF}|\u{1F1E0}-\u{1F1FF}|\u{1F900}-\u{1F9FF}|\u{1F100}-\u{1F1FF}|\u{1F200}-\u{1F2FF}|\u{2100}-\u{26FF}]/gu, '');
880
+ }
881
+ function removeAllEmojis(text) {
882
+ // More comprehensive regex that catches:
883
+ // 1. Standard emojis
884
+ // 2. Emoji variations and modifiers
885
+ // 3. Symbol characters
886
+ // 4. Pictographs
887
+ // 5. Emoticons
888
+ // 6. Regional indicators
889
+ // 7. Flags
890
+ 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, '');
891
+ }
892
+
893
+ const ICONS = {
894
+ gear: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
895
+ <path d="M12 1.75a1.25 1.25 0 0 1 1.25 1.25v.917a8.456 8.456 0 0 1 2.334.98l.645-.647a1.25 1.25 0 1 1 1.768 1.768l-.646.645c.389.69.729 1.437.98 2.334h.917a1.25 1.25 0 0 1 0 2.5h-.917a8.456 8.456 0 0 1-.98 2.334l.646.645a1.25 1.25 0 1 1-1.768 1.768l-.645-.646a8.456 8.456 0 0 1-2.334.98v.917a1.25 1.25 0 0 1-2.5 0v-.917a8.456 8.456 0 0 1-2.334-.98l-.645.646a1.25 1.25 0 1 1-1.768-1.768l.646-.645a8.456 8.456 0 0 1-.98-2.334H1.75a1.25 1.25 0 0 1 0-2.5h.917a8.456 8.456 0 0 1 .98-2.334l-.646-.645a1.25 1.25 0 1 1 1.768-1.768l.645.647c.69-.389 1.437-.729 2.334-.98V3a1.25 1.25 0 0 1 1.25-1.25h.083ZM12 7.5a4.5 4.5 0 1 0 0 9 4.5 4.5 0 0 0 0-9Z"/>
896
+ </svg>
897
+ `,
898
+ chat: `<svg viewBox="0 0 24 24" fill="currentColor">
899
+ <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
900
+ </svg>`,
901
+ play: `<svg
902
+ xmlns="http://www.w3.org/2000/svg"
903
+ width="16"
904
+ height="16"
905
+ viewBox="0 0 24 24"
906
+ fill="none"
907
+ stroke="#263042"
908
+ stroke-width="2"
909
+ stroke-linecap="round"
910
+ stroke-linejoin="round">
911
+ <circle cx="12" cy="12" r="10"></circle>
912
+ <polygon points="10 8 16 12 10 16 10 8"></polygon>
913
+ </svg>`,
914
+ loading: `<svg
915
+ xmlns="http://www.w3.org/2000/svg"
916
+ width="16"
917
+ height="16"
918
+ viewBox="0 0 24 24"
919
+ fill="none"
920
+ stroke="#263042"
921
+ stroke-width="2"
922
+ stroke-linecap="round"
923
+ stroke-linejoin="round">
924
+ <line x1="12" y1="2" x2="12" y2="6"></line>
925
+ <line x1="12" y1="18" x2="12" y2="22"></line>
926
+ <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
927
+ <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
928
+ <line x1="2" y1="12" x2="6" y2="12"></line>
929
+ <line x1="18" y1="12" x2="22" y2="12"></line>
930
+ <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
931
+ <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
932
+ </svg>`,
933
+ user: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
934
+ <!-- Head circle -->
935
+ <circle cx="12" cy="8" r="4" fill="currentColor"/>
936
+
937
+ <!-- Body shape -->
938
+ <path d="M20 19v1a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-1c0-3.87 3.13-7 7-7h2c3.87 0 7 3.13 7 7z" fill="currentColor"/>
939
+ </svg>`,
940
+ ai: `<svg
941
+ xmlns="http://www.w3.org/2000/svg"
942
+ viewBox="0 0 100 100"
943
+ width="100"
944
+ height="100"
945
+ fill="none"
946
+ stroke="black"
947
+ stroke-width="2"
948
+ >
949
+ <!-- Brain outline -->
950
+ <path
951
+ d="M50 10
952
+ C60 10, 70 20, 70 30
953
+ S60 50, 50 50
954
+ S30 40, 30 30
955
+ S40 10, 50 10Z"
956
+ fill="#B3E5FC"
957
+ />
958
+
959
+ <!-- Circuit lines -->
960
+ <line x1="50" y1="50" x2="50" y2="70" stroke="#0288D1" stroke-width="2" />
961
+ <line x1="50" y1="70" x2="40" y2="80" stroke="#0288D1" stroke-width="2" />
962
+ <line x1="50" y1="70" x2="60" y2="80" stroke="#0288D1" stroke-width="2" />
963
+
964
+ <!-- Nodes -->
965
+ <circle cx="50" cy="70" r="2" fill="#0288D1" />
966
+ <circle cx="40" cy="80" r="3" fill="#0288D1" />
967
+ <circle cx="60" cy="80" r="3" fill="#0288D1" />
968
+ </svg>
969
+ `,
970
+ };
971
+
972
+ class IconsComponent {
973
+ constructor(sanitizer) {
974
+ this.sanitizer = sanitizer;
975
+ this.size = 14;
976
+ this.color = 'currentColor';
977
+ }
978
+ ngOnChanges(changes) {
979
+ if (changes['name']) {
980
+ const svg = ICONS[this.name] || '';
981
+ this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
982
+ }
983
+ }
984
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
985
+ 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: `
986
+ <span
987
+ [innerHTML]="sanitizedIcon"
988
+ [style.display]="'inline-flex'"
989
+ [style.alignItems]="'center'"
990
+ [style.justifyContent]="'center'"
991
+ [style.width.px]="size"
992
+ [style.height.px]="size"
993
+ [style.color]="color"></span>
994
+ `, 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"] }); }
995
+ }
996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, decorators: [{
997
+ type: Component,
998
+ args: [{ selector: 'dc-icon', template: `
999
+ <span
1000
+ [innerHTML]="sanitizedIcon"
1001
+ [style.display]="'inline-flex'"
1002
+ [style.alignItems]="'center'"
1003
+ [style.justifyContent]="'center'"
1004
+ [style.width.px]="size"
1005
+ [style.height.px]="size"
1006
+ [style.color]="color"></span>
1007
+ `, 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"] }]
1008
+ }], ctorParameters: () => [{ type: i1.DomSanitizer }], propDecorators: { name: [{
1009
+ type: Input
1010
+ }], size: [{
1011
+ type: Input
1012
+ }], color: [{
1013
+ type: Input
1014
+ }] } });
1015
+
1016
+ class AudioTextSyncService {
1017
+ constructor() {
1018
+ // Signal-based API
1019
+ this.highlightedWordsSignal = signal([]);
1020
+ // Observable-based API
1021
+ this.highlightedWords$ = new BehaviorSubject([]);
1022
+ this.cleanup$ = new Subject();
1023
+ this.activeAudio = null;
1024
+ this.destroyRef = inject(DestroyRef);
1025
+ // Ensure cleanup when service is destroyed
1026
+ this.destroyRef.onDestroy(() => {
1027
+ this.stopSync();
1028
+ });
1029
+ }
1030
+ /**
1031
+ * Synchronizes audio playback with text transcription
1032
+ * @param audioElement The audio element to sync with
1033
+ * @param transcriptionTimestamps Array of word timestamps
1034
+ */
1035
+ syncAudioWithText(audioElement, transcriptionTimestamps) {
1036
+ // Stop any existing sync
1037
+ this.stopSync();
1038
+ this.activeAudio = audioElement;
1039
+ // Initialize the highlighted words state
1040
+ const initialWords = transcriptionTimestamps.map((word, index) => ({
1041
+ word: word.word,
1042
+ index,
1043
+ isHighlighted: false,
1044
+ }));
1045
+ this.highlightedWordsSignal.set(initialWords);
1046
+ this.highlightedWords$.next(initialWords);
1047
+ // Listen to timeupdate events
1048
+ fromEvent(audioElement, 'timeupdate')
1049
+ .pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.cleanup$), map(() => audioElement.currentTime))
1050
+ .subscribe((currentTime) => {
1051
+ const updatedWords = transcriptionTimestamps.map((word, index) => {
1052
+ const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
1053
+ return {
1054
+ word: word.word,
1055
+ index,
1056
+ isHighlighted,
1057
+ };
1058
+ });
1059
+ // Update both signal and observable
1060
+ this.highlightedWordsSignal.set(updatedWords);
1061
+ this.highlightedWords$.next(updatedWords);
1062
+ });
1063
+ // Listen to ended event for cleanup
1064
+ fromEvent(audioElement, 'ended')
1065
+ .pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.cleanup$))
1066
+ .subscribe(() => {
1067
+ // Reset highlighting when audio ends
1068
+ const resetWords = initialWords.map((word) => ({
1069
+ ...word,
1070
+ isHighlighted: false,
1071
+ }));
1072
+ this.highlightedWordsSignal.set(resetWords);
1073
+ this.highlightedWords$.next(resetWords);
1074
+ });
1075
+ }
1076
+ /**
1077
+ * Stops the current sync and cleans up resources
1078
+ */
1079
+ stopSync() {
1080
+ if (this.activeAudio) {
1081
+ this.cleanup$.next();
1082
+ this.activeAudio = null;
1083
+ // Reset state
1084
+ this.highlightedWordsSignal.set([]);
1085
+ this.highlightedWords$.next([]);
1086
+ }
1087
+ }
1088
+ /**
1089
+ * Returns the current highlighted words as a signal
1090
+ */
1091
+ getHighlightedWordsSignal() {
1092
+ return this.highlightedWordsSignal;
1093
+ }
1094
+ /**
1095
+ * Returns the current highlighted words as an observable
1096
+ */
1097
+ getHighlightedWords$() {
1098
+ return this.highlightedWords$.asObservable();
1099
+ }
1100
+ /**
1101
+ * Checks if a word at a specific index is currently highlighted
1102
+ * @param index The index of the word to check
1103
+ */
1104
+ isWordHighlighted(index) {
1105
+ return this.highlightedWordsSignal().some((word) => word.index === index && word.isHighlighted);
1106
+ }
1107
+ /**
1108
+ * Returns an observable that emits true when a word at a specific index is highlighted
1109
+ * @param index The index of the word to observe
1110
+ */
1111
+ isWordHighlighted$(index) {
1112
+ return this.highlightedWords$.pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
1113
+ }
1114
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1115
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, providedIn: 'root' }); }
1116
+ }
1117
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, decorators: [{
1118
+ type: Injectable,
1119
+ args: [{
1120
+ providedIn: 'root',
1121
+ }]
1122
+ }], ctorParameters: () => [] });
1123
+
1124
+ class MessageContentComponent {
1125
+ constructor(cdr, audioTextSyncService) {
1126
+ this.cdr = cdr;
1127
+ this.audioTextSyncService = audioTextSyncService;
1128
+ this.isLoading = false;
1129
+ this.playAudio = new EventEmitter();
1130
+ // Get the highlighted words signal from the service
1131
+ this.highlightedWords = this.audioTextSyncService.getHighlightedWordsSignal();
1132
+ // Setup effect to mark for check when highlighted words change
1133
+ effect(() => {
1134
+ // Just accessing the signal in an effect will re-run the effect when the signal changes
1135
+ this.highlightedWords();
1136
+ // Mark for check to ensure the component updates
1137
+ this.cdr.markForCheck();
1138
+ });
1139
+ }
1140
+ get hasTranscription() {
1141
+ return !!this.message.transcriptionTimestamps;
1142
+ }
1143
+ get messageText() {
1144
+ return this.message.content || this.message.text;
1145
+ }
1146
+ get messageTag() {
1147
+ return this.message.tag || null;
1148
+ }
1149
+ onPlayMessage() {
1150
+ this.playAudio.emit(this.message);
1151
+ }
1152
+ /**
1153
+ * Track function for ngFor to improve performance
1154
+ * @param index The current item's index
1155
+ * @param item The current item
1156
+ */
1157
+ trackByIndex(index, item) {
1158
+ return index;
1159
+ }
1160
+ ngOnInit() {
1161
+ console.log(this.message);
1162
+ }
1163
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageContentComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: AudioTextSyncService }], target: i0.ɵɵFactoryTarget.Component }); }
1164
+ 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<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n }\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]=\"messageTag\" [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()\">\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$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
987
1165
  }
1166
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageContentComponent, decorators: [{
1167
+ type: Component,
1168
+ args: [{ selector: 'dc-message-content', standalone: true, imports: [CommonModule, IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Message with transcription -->\n@if (hasTranscription) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n }\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]=\"messageTag\" [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()\">\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"] }]
1169
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: AudioTextSyncService }], propDecorators: { message: [{
1170
+ type: Input
1171
+ }], isLoading: [{
1172
+ type: Input
1173
+ }], playAudio: [{
1174
+ type: Output
1175
+ }] } });
988
1176
 
989
1177
  function markdownToHTML(markdownText) {
990
1178
  // Convert italics-bold (***text***)
@@ -1045,44 +1233,100 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
1045
1233
  }]
1046
1234
  }] });
1047
1235
 
1236
+ class MultiMessageContentComponent {
1237
+ constructor() {
1238
+ this.isLoading = false;
1239
+ this.playAudio = new EventEmitter();
1240
+ }
1241
+ onPlayMessage(message) {
1242
+ this.playAudio.emit(message);
1243
+ }
1244
+ ngOnInit() {
1245
+ console.log(this.messages);
1246
+ debugger;
1247
+ }
1248
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiMessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1249
+ 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 (!!message.transcriptionTimestamps) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <span *ngFor=\"let word of message.transcriptionTimestamps\" [class.highlight]=\"word.highlighted\">{{ word.word }}</span>\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$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1250
+ }
1251
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiMessageContentComponent, decorators: [{
1252
+ type: Component,
1253
+ args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [CommonModule, IconsComponent, SimpleMdToHtmlPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messages; track message) {\n<!-- Message with transcription -->\n@if (!!message.transcriptionTimestamps) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <span *ngFor=\"let word of message.transcriptionTimestamps\" [class.highlight]=\"word.highlighted\">{{ word.word }}</span>\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"] }]
1254
+ }], propDecorators: { messages: [{
1255
+ type: Input
1256
+ }], isLoading: [{
1257
+ type: Input
1258
+ }], playAudio: [{
1259
+ type: Output
1260
+ }] } });
1261
+
1262
+ function matchTranscription(originalText, transcription) {
1263
+ const result = [];
1264
+ const transcriptionMap = new Map();
1265
+ // Create a map of lowercase words to an array of their corresponding objects in transcription
1266
+ for (const obj of transcription) {
1267
+ const lowercaseWord = obj.word.trim().toLowerCase();
1268
+ if (transcriptionMap.has(lowercaseWord)) {
1269
+ transcriptionMap.get(lowercaseWord).push(obj);
1270
+ }
1271
+ else {
1272
+ transcriptionMap.set(lowercaseWord, [obj]);
1273
+ }
1274
+ }
1275
+ // Split the original text into an array of words
1276
+ const words = originalText.split(' ');
1277
+ let currentTime = 0;
1278
+ for (const word of words) {
1279
+ const lowercaseWord = word.toLowerCase();
1280
+ const matchedObjs = transcriptionMap.get(lowercaseWord);
1281
+ if (matchedObjs && matchedObjs.length > 0) {
1282
+ const matchedObj = matchedObjs.shift();
1283
+ result.push({
1284
+ word: word,
1285
+ start: Number(matchedObj.start.toFixed(3)),
1286
+ end: Number(matchedObj.end.toFixed(3)),
1287
+ });
1288
+ currentTime = matchedObj.end;
1289
+ }
1290
+ else {
1291
+ result.push({
1292
+ word: word,
1293
+ start: Number(currentTime.toFixed(3)),
1294
+ end: Number(currentTime.toFixed(3)),
1295
+ });
1296
+ }
1297
+ }
1298
+ return result;
1299
+ }
1300
+ function extractAudioAndTranscription(message, audio) {
1301
+ message.audioUrl = audio.blobUrl;
1302
+ message.transcription = audio.transcription;
1303
+ if (message.transcription) {
1304
+ message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
1305
+ }
1306
+ }
1307
+
1048
1308
  class ChatMessageComponent {
1049
- constructor(audioService, agentCardService, cdr) {
1309
+ constructor(audioService, agentCardService, audioTextSyncService) {
1050
1310
  this.audioService = audioService;
1051
1311
  this.agentCardService = agentCardService;
1052
- this.cdr = cdr;
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
- };
1312
+ this.audioTextSyncService = audioTextSyncService;
1313
+ this.destroyRef = inject(DestroyRef);
1314
+ this.conversationChatSettings = signal(null);
1315
+ this.isLoading = signal(false);
1068
1316
  }
1069
1317
  async ngOnInit() {
1070
- // this.conversationSettings = { ...this.conversationSettings, ...settings };
1071
- this.conversationChatSettings = await this.agentCardService.getConversationUserChatSettings();
1072
- if (this.chatMessage.role == ChatRole.AssistantHelper) {
1318
+ const settings = await this.agentCardService.getConversationUserChatSettings();
1319
+ this.conversationChatSettings.set(settings);
1320
+ if (this.chatMessage.role === ChatRole.AssistantHelper) {
1073
1321
  return;
1074
1322
  }
1075
- if (this.chatMessage.role == ChatRole.User) {
1076
- if (this.conversationChatSettings?.repeatRecording && this.conversationChatSettings?.synthVoice) {
1323
+ const settings$ = this.conversationChatSettings();
1324
+ if (this.chatMessage.role === ChatRole.User) {
1325
+ if (settings$?.repeatRecording && settings$?.synthVoice) {
1077
1326
  this.playMessage(this.chatMessage);
1078
1327
  }
1079
1328
  }
1080
- else {
1081
- if (!this.conversationChatSettings?.synthVoice) {
1082
- return;
1083
- }
1084
- console.log(this.chatMessage.multiMessages);
1085
- // this is an assistant message
1329
+ else if (settings$?.synthVoice) {
1086
1330
  if (this.chatMessage.multiMessages) {
1087
1331
  this.generateAndPlayAllAudios(this.chatMessage.multiMessages);
1088
1332
  }
@@ -1091,173 +1335,244 @@ class ChatMessageComponent {
1091
1335
  }
1092
1336
  }
1093
1337
  }
1094
- ngOnDestroy() {
1095
- this.isDestroyed = true;
1096
- }
1097
- async generateAndPlayAudio(message, overwritetext = null) {
1098
- const text = overwritetext || message.content;
1099
- const ttsObject = this.getTTSRequest({ ...this.chatMessage, text });
1100
- if (message.ssml) {
1101
- ttsObject.ssml = message.ssml;
1102
- }
1103
- const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
1104
- this.extractAudioAndTranscription(message, speechAudio);
1105
- this.playMessage(message);
1106
- // TODO: cuando no sirve el server utilizar esta voz del navegador
1107
- // else if (this.selectedAudioType === 'default') {
1108
- // await this.speechService.speach(message.content, { speed: 1, tone: 0.9 });
1109
- // }
1110
- }
1111
- extractAudioAndTranscription(message, audio) {
1112
- message.audioUrl = audio.blobUrl;
1113
- message.transcription = audio.transcription;
1114
- if (message.transcription) {
1115
- message.transcriptionTimestamps = this.matchTranscription(message.text || message.content, message.transcription.words);
1338
+ async generateAndPlayAudio(message, overwriteText = null) {
1339
+ this.isLoading.set(true);
1340
+ try {
1341
+ const text = overwriteText || message.content;
1342
+ const ttsObject = this.buildObjectTTSRequest({ ...this.chatMessage, text });
1343
+ if (message.ssml) {
1344
+ ttsObject.ssml = message.ssml;
1345
+ }
1346
+ const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
1347
+ extractAudioAndTranscription(message, speechAudio);
1348
+ this.playMessage(message);
1349
+ }
1350
+ finally {
1351
+ this.isLoading.set(false);
1116
1352
  }
1117
1353
  }
1118
1354
  playMessage(message) {
1119
- console.log('playMessage', message);
1120
- if (message.audioUrl) {
1121
- const audioHtml = this.audioService.playAudio(message.audioUrl);
1122
- message['audioHtml'] = audioHtml;
1123
- this.subscribeToTimeUpdate(audioHtml, message.transcriptionTimestamps);
1124
- return audioHtml;
1355
+ if (!message.audioUrl) {
1356
+ return null;
1125
1357
  }
1126
- else {
1127
- // this.speechService.speach(message.content);
1358
+ const audioElement = this.audioService.playAudio(message.audioUrl);
1359
+ message['audioHtml'] = audioElement;
1360
+ if (message.transcriptionTimestamps) {
1361
+ // Use the audio-text sync service instead of direct manipulation
1362
+ this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps);
1128
1363
  }
1129
- return null;
1364
+ return audioElement;
1130
1365
  }
1131
- subscribeToTimeUpdate(audioHtml, transcriptionTimestamps = null) {
1132
- console.log('subscribing to time updates', audioHtml, transcriptionTimestamps);
1133
- if (transcriptionTimestamps) {
1134
- audioHtml.ontimeupdate = (time) => {
1135
- transcriptionTimestamps.forEach((word) => {
1136
- word.highlighted = audioHtml.currentTime >= word.start - 0.15 && audioHtml.currentTime < word.end + 0.15;
1137
- console.log('highliting word', word, 'enable detecting');
1138
- this.cdr.detectChanges();
1139
- });
1140
- };
1141
- }
1142
- audioHtml.onended = () => {
1143
- audioHtml.ontimeupdate = null;
1144
- audioHtml = null;
1145
- // this.chatBroker.currentMessagePlayed$.next(true);
1146
- };
1366
+ // This method is no longer needed as we're using the AudioTextSyncService
1367
+ // It's kept here as a reference but can be removed
1368
+ /*
1369
+ private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
1370
+ // This functionality has been moved to AudioTextSyncService
1147
1371
  }
1372
+ */
1148
1373
  generateAndPlayAllAudios(multiMessages) {
1374
+ if (!multiMessages.length)
1375
+ return;
1149
1376
  let currentIndex = 0;
1150
1377
  const playAudioSequentially = async () => {
1151
- if (this.isDestroyed)
1152
- return;
1153
1378
  if (currentIndex >= multiMessages.length) {
1154
- console.log('All audio files have been played.');
1155
1379
  return;
1156
1380
  }
1157
- const msn = multiMessages[currentIndex];
1158
- // Audio is processed in ahead at last, first time will wait for audio, the rest is only wait,
1159
- if (!msn.audioUrl) {
1160
- if (msn.audioPromise) {
1161
- await msn.audioPromise;
1381
+ const currentMessage = multiMessages[currentIndex];
1382
+ currentMessage.isLoading = true;
1383
+ try {
1384
+ debugger;
1385
+ // Process current message audio if needed
1386
+ if (!currentMessage.audioUrl) {
1387
+ if (currentMessage.audioPromise) {
1388
+ await currentMessage.audioPromise;
1389
+ }
1390
+ else {
1391
+ const request = this.buildObjectTTSRequest(currentMessage);
1392
+ currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
1393
+ const audio = await currentMessage.audioPromise;
1394
+ extractAudioAndTranscription(currentMessage, audio);
1395
+ }
1396
+ }
1397
+ // Play the current message
1398
+ const audioElement = this.playMessage(currentMessage);
1399
+ if (audioElement) {
1400
+ audioElement.addEventListener('ended', () => {
1401
+ currentIndex++;
1402
+ playAudioSequentially();
1403
+ });
1162
1404
  }
1163
1405
  else {
1164
- const request = this.getTTSRequest(msn);
1165
- msn.isLoading = true;
1166
- this.cdr.detectChanges();
1167
- msn.audioPromise = this.agentCardService.getTextAudioFile(request);
1168
- const audio = await msn.audioPromise;
1169
- this.extractAudioAndTranscription(msn, audio);
1170
- msn.isLoading = false;
1171
- this.cdr.detectChanges();
1406
+ // If playback failed, move to next message
1407
+ currentIndex++;
1408
+ playAudioSequentially();
1172
1409
  }
1410
+ // Preload next message audio
1411
+ this.preloadNextMessageAudio(multiMessages, currentIndex);
1173
1412
  }
1174
- const audioHtml = this.playMessage(msn);
1175
- audioHtml.onended = () => {
1176
- currentIndex++;
1177
- playAudioSequentially();
1178
- };
1179
- // Load the next audio file in advance
1180
- if (currentIndex + 1 < multiMessages.length) {
1181
- const nextMessage = multiMessages[currentIndex + 1];
1182
- const request = this.getTTSRequest(nextMessage);
1413
+ finally {
1414
+ currentMessage.isLoading = false;
1415
+ }
1416
+ };
1417
+ // Start the sequential playback
1418
+ playAudioSequentially();
1419
+ }
1420
+ preloadNextMessageAudio(messages, currentIndex) {
1421
+ if (currentIndex + 1 < messages.length) {
1422
+ const nextMessage = messages[currentIndex + 1];
1423
+ if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
1424
+ const request = this.buildObjectTTSRequest(nextMessage);
1183
1425
  nextMessage.isLoading = true;
1184
1426
  nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
1185
1427
  nextMessage.audioPromise.then((audio) => {
1186
- this.extractAudioAndTranscription(nextMessage, audio);
1428
+ extractAudioAndTranscription(nextMessage, audio);
1187
1429
  nextMessage.isLoading = false;
1188
1430
  });
1189
1431
  }
1190
- };
1191
- playAudioSequentially();
1432
+ }
1192
1433
  }
1193
- getTTSRequest(nextMessage) {
1194
- // const settingsConv = this.userService.getUserSnapshot().settings.conversation;
1195
- const generateTranscription = this.conversationChatSettings?.highlightWords ?? false;
1196
- const speedRate = this.conversationChatSettings?.speedRate || 0;
1197
- const speed = typeof this.conversationChatSettings?.speed === 'string' ? this.conversationChatSettings.speed : AudioSpeed.Regular;
1198
- const text = removeEmojis(nextMessage.text);
1199
- const data = {
1200
- text: text,
1201
- voice: nextMessage.voice || this.chatMessage.voice,
1434
+ buildObjectTTSRequest(message) {
1435
+ const settings = this.conversationChatSettings();
1436
+ const generateTranscription = settings?.highlightWords ?? false;
1437
+ const speedRate = settings?.speedRate || 0;
1438
+ const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed.Regular;
1439
+ const text = removeEmojis(message.text || message.content);
1440
+ return {
1441
+ text,
1442
+ voice: message.voice || this.chatMessage.voice,
1202
1443
  generateTranscription,
1203
1444
  speedRate,
1204
1445
  speed,
1205
1446
  };
1206
- return data;
1207
- }
1208
- matchTranscription(originalText, transcription) {
1209
- const result = [];
1210
- const transcriptionMap = new Map();
1211
- // Create a map of lowercase words to an array of their corresponding objects in transcription
1212
- for (const obj of transcription) {
1213
- const lowercaseWord = obj.word.trim().toLowerCase();
1214
- if (transcriptionMap.has(lowercaseWord)) {
1215
- transcriptionMap.get(lowercaseWord).push(obj);
1216
- }
1217
- else {
1218
- transcriptionMap.set(lowercaseWord, [obj]);
1219
- }
1220
- }
1221
- // Split the original text into an array of words
1222
- const words = originalText.split(' ');
1223
- let currentTime = 0;
1224
- for (const word of words) {
1225
- const lowercaseWord = word.toLowerCase();
1226
- const matchedObjs = transcriptionMap.get(lowercaseWord);
1227
- if (matchedObjs && matchedObjs.length > 0) {
1228
- const matchedObj = matchedObjs.shift();
1229
- result.push({
1230
- word: word,
1231
- start: matchedObj.start,
1232
- end: matchedObj.end,
1233
- });
1234
- currentTime = matchedObj.end;
1235
- }
1236
- else {
1237
- result.push({
1238
- word: word,
1239
- start: currentTime,
1240
- end: currentTime,
1241
- });
1242
- }
1243
- }
1244
- return result;
1245
1447
  }
1246
- 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: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1247
- 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: "<span *ngIf=\"chatMessage?.multiMessages\">\n <span style=\"line-height: 1.5\" *ngFor=\"let message of chatMessage.multiMessages\">\n <div [ngClass]=\"message.tag\" *ngIf=\"message.transcriptionTimestamps\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: message.isLoading, message: message }\"></ng-container>\n <span *ngFor=\"let word of message.transcriptionTimestamps\" [class.highlight]=\"word.highlighted\">\n {{ word.word }}\n </span>\n </div>\n\n <div *ngIf=\"!message.transcriptionTimestamps\">\n <div (click)=\"playMessage(message)\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: message.isLoading, message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text\"> </span>\n </div>\n </div>\n </span>\n</span>\n\n@if (!chatMessage?.multiMessages) {\n<span>\n @if (chatMessage.transcriptionTimestamps) {\n <div>\n <span *ngFor=\"let word of chatMessage.transcriptionTimestamps\" [class.highlight]=\"word.highlighted\">\n {{ word.word }}\n </span>\n </div>\n\n @if (!chatMessage.transcriptionTimestamps) {\n <div>\n <span (click)=\"playMessage(chatMessage)\" [innerHTML]=\"chatMessage.content | simpleMdToHtml\"></span>\n </div>\n }}\n</span>\n}\n\n<div class=\"small\" *ngIf=\"chatMessage.translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n\n<ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i *ngIf=\"isLoading\" (click)=\"playMessage(message)\">\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n </i>\n\n <i *ngIf=\"!isLoading\" (click)=\"playMessage(message)\">\n <dc-icon name=\"play\"></dc-icon>\n </i>\n</ng-template>\n", styles: [":host{display:block}.highlight{background-color:#fff8a7;color:#011630;text-decoration:underline #59a4ff solid 3px}::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}.small{margin-top:8x;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}.spin-animation{display:inline-block;animation:spin 2s linear infinite;transform-origin:center center;transform-box:fill-box;vertical-align:middle}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "pipe", type: SimpleMdToHtmlPipe, name: "simpleMdToHtml" }] }); }
1448
+ 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 }); }
1449
+ 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 }); }
1248
1450
  }
1249
1451
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, decorators: [{
1250
1452
  type: Component,
1251
- args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, IconsComponent, SimpleMdToHtmlPipe], template: "<span *ngIf=\"chatMessage?.multiMessages\">\n <span style=\"line-height: 1.5\" *ngFor=\"let message of chatMessage.multiMessages\">\n <div [ngClass]=\"message.tag\" *ngIf=\"message.transcriptionTimestamps\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: message.isLoading, message: message }\"></ng-container>\n <span *ngFor=\"let word of message.transcriptionTimestamps\" [class.highlight]=\"word.highlighted\">\n {{ word.word }}\n </span>\n </div>\n\n <div *ngIf=\"!message.transcriptionTimestamps\">\n <div (click)=\"playMessage(message)\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: message.isLoading, message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text\"> </span>\n </div>\n </div>\n </span>\n</span>\n\n@if (!chatMessage?.multiMessages) {\n<span>\n @if (chatMessage.transcriptionTimestamps) {\n <div>\n <span *ngFor=\"let word of chatMessage.transcriptionTimestamps\" [class.highlight]=\"word.highlighted\">\n {{ word.word }}\n </span>\n </div>\n\n @if (!chatMessage.transcriptionTimestamps) {\n <div>\n <span (click)=\"playMessage(chatMessage)\" [innerHTML]=\"chatMessage.content | simpleMdToHtml\"></span>\n </div>\n }}\n</span>\n}\n\n<div class=\"small\" *ngIf=\"chatMessage.translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n\n<ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i *ngIf=\"isLoading\" (click)=\"playMessage(message)\">\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n </i>\n\n <i *ngIf=\"!isLoading\" (click)=\"playMessage(message)\">\n <dc-icon name=\"play\"></dc-icon>\n </i>\n</ng-template>\n", styles: [":host{display:block}.highlight{background-color:#fff8a7;color:#011630;text-decoration:underline #59a4ff solid 3px}::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}.small{margin-top:8x;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}.spin-animation{display:inline-block;animation:spin 2s linear infinite;transform-origin:center center;transform-box:fill-box;vertical-align:middle}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
1453
+ 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"] }]
1252
1454
  }], ctorParameters: () => [{ type: AudioService }, { type: AgentCardsAbstractService, decorators: [{
1253
1455
  type: Inject,
1254
1456
  args: [CONVERSATION_AI_TOKEN]
1255
- }] }, { type: i0.ChangeDetectorRef }], propDecorators: { chatMessage: [{
1457
+ }] }, { type: AudioTextSyncService }], propDecorators: { chatMessage: [{
1256
1458
  type: Input
1257
1459
  }], chatUserSettings: [{
1258
1460
  type: Input
1259
1461
  }] } });
1260
1462
 
1463
+ class ChatHeaderComponent {
1464
+ constructor(agentCardService) {
1465
+ this.agentCardService = agentCardService;
1466
+ this.isAdmin = false;
1467
+ this.alternativeConversation = [];
1468
+ this.restartConversationEvent = new EventEmitter();
1469
+ this.showInfoEvent = new EventEmitter();
1470
+ this.settingsClickEvent = new EventEmitter();
1471
+ }
1472
+ restartConversation(conversation = null) {
1473
+ this.restartConversationEvent.emit(conversation);
1474
+ }
1475
+ showInfo() {
1476
+ this.showInfoEvent.emit();
1477
+ }
1478
+ settingsClick() {
1479
+ this.settingsClickEvent.emit();
1480
+ }
1481
+ async changeConversationCard() {
1482
+ const response = prompt('¿Qué conversación quieres usar? Escribe el titulo ejemplo: word_reflection_level_1_base_es');
1483
+ if (response) {
1484
+ const filters = {
1485
+ filters: {
1486
+ title: { $regex: response },
1487
+ },
1488
+ };
1489
+ console.log('filters', filters);
1490
+ const conversationCards = await this.agentCardService.filterConversationCards(filters);
1491
+ console.log('conversationCards', conversationCards);
1492
+ this.alternativeConversation = conversationCards.rows;
1493
+ }
1494
+ }
1495
+ 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 }); }
1496
+ 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" }, outputs: { restartConversationEvent: "restartConversationEvent", showInfoEvent: "showInfoEvent", settingsClickEvent: "settingsClickEvent" }, ngImport: i0, template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">Reiniciar conversaci\u00F3n</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 }] }); }
1497
+ }
1498
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatHeaderComponent, decorators: [{
1499
+ type: Component,
1500
+ args: [{ selector: 'dc-chat-header', standalone: true, imports: [CommonModule], template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">Reiniciar conversaci\u00F3n</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"] }]
1501
+ }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
1502
+ type: Inject,
1503
+ args: [CONVERSATION_AI_TOKEN]
1504
+ }] }], propDecorators: { isAdmin: [{
1505
+ type: Input
1506
+ }], alternativeConversation: [{
1507
+ type: Input
1508
+ }], restartConversationEvent: [{
1509
+ type: Output
1510
+ }], showInfoEvent: [{
1511
+ type: Output
1512
+ }], settingsClickEvent: [{
1513
+ type: Output
1514
+ }] } });
1515
+
1516
+ class ChatFooterComponent {
1517
+ constructor() {
1518
+ this.isAIThinking = false;
1519
+ this.score = 0;
1520
+ this.micSettings = { useWhisper: true, lang: 'en' };
1521
+ this.sendMessage = new EventEmitter();
1522
+ this.textInputChanged = new EventEmitter();
1523
+ this.micFinishedEvent = new EventEmitter();
1524
+ this.chatInputControl = new FormControl();
1525
+ }
1526
+ /**
1527
+ * Sets the input text in the textarea
1528
+ * @param text The text to set
1529
+ */
1530
+ setInputText(text) {
1531
+ this.chatInputControl.setValue(text);
1532
+ this.textInputChanged.emit(text);
1533
+ }
1534
+ /**
1535
+ * Handles the mic finished event
1536
+ * @param eventBlob The blob event from the mic component
1537
+ */
1538
+ micFinished(eventBlob) {
1539
+ this.micFinishedEvent.emit(eventBlob);
1540
+ }
1541
+ /**
1542
+ * Sends the user message
1543
+ */
1544
+ sendUserMessage() {
1545
+ if (this.isAIThinking || !this.chatInputControl.value) {
1546
+ return;
1547
+ }
1548
+ const text = this.chatInputControl.value;
1549
+ const message = {
1550
+ content: text,
1551
+ role: ChatRole.User,
1552
+ };
1553
+ this.sendMessage.emit(message);
1554
+ this.chatInputControl.setValue('');
1555
+ }
1556
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1557
+ 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$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.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"] }] }); }
1558
+ }
1559
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, decorators: [{
1560
+ type: Component,
1561
+ 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"] }]
1562
+ }], propDecorators: { isAIThinking: [{
1563
+ type: Input
1564
+ }], score: [{
1565
+ type: Input
1566
+ }], micSettings: [{
1567
+ type: Input
1568
+ }], sendMessage: [{
1569
+ type: Output
1570
+ }], textInputChanged: [{
1571
+ type: Output
1572
+ }], micFinishedEvent: [{
1573
+ type: Output
1574
+ }] } });
1575
+
1261
1576
  const SpeedDescription = {
1262
1577
  1: 'Muy Lento',
1263
1578
  2: 'Lento',
@@ -1348,7 +1663,7 @@ class ProviderSelectorComponent {
1348
1663
  }
1349
1664
  }
1350
1665
  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 }); }
1351
- 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: i2.DecimalPipe, name: "number" }, { kind: "pipe", type: i2.DatePipe, name: "date" }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i3.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.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: i6.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { 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: "pipe", type: TruncatePipe, name: "truncate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1666
+ 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$1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.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.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$1.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 }); }
1352
1667
  }
1353
1668
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ProviderSelectorComponent, decorators: [{
1354
1669
  type: Component,
@@ -1420,8 +1735,8 @@ class DCConversationUserChatSettingsComponent {
1420
1735
  this.dialogRef.close(this.form.value);
1421
1736
  }
1422
1737
  }
1423
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [{ token: i1$2.DynamicDialogRef }, { token: i1$1.FormBuilder }, { token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
1424
- 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: 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.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.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: "directive", type: i4.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: SliderModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i3.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$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: "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"] }] }); }
1738
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [{ token: i1$3.DynamicDialogRef }, { token: i1$2.FormBuilder }, { token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
1739
+ 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$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.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.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"] }] }); }
1425
1740
  }
1426
1741
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
1427
1742
  type: Component,
@@ -1440,7 +1755,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
1440
1755
  TooltipModule,
1441
1756
  ProviderSelectorComponent,
1442
1757
  ], 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"] }]
1443
- }], ctorParameters: () => [{ type: i1$2.DynamicDialogRef }, { type: i1$1.FormBuilder }, { type: AgentCardsAbstractService, decorators: [{
1758
+ }], ctorParameters: () => [{ type: i1$3.DynamicDialogRef }, { type: i1$2.FormBuilder }, { type: AgentCardsAbstractService, decorators: [{
1444
1759
  type: Inject,
1445
1760
  args: [CONVERSATION_AI_TOKEN]
1446
1761
  }] }], propDecorators: { showFeature: [{
@@ -1477,6 +1792,7 @@ const DefaultEvaluatorAgentCard = {
1477
1792
  model: { id: 'gpt-4o-mini', provider: 'openai' },
1478
1793
  sources: [],
1479
1794
  };
1795
+
1480
1796
  class DCChatComponent {
1481
1797
  constructor(agentCardService, toastService, conversationBuilder, dialogService, cdr) {
1482
1798
  this.agentCardService = agentCardService;
@@ -1491,11 +1807,9 @@ class DCChatComponent {
1491
1807
  this.thinkingImg = `data:image/svg+xml;base64,${btoa(decodeURIComponent(ICONS.gear))}`;
1492
1808
  this.isDestroyed = false;
1493
1809
  this.score = 10;
1494
- // @ViewChild('textSelectedDiv') textSelectedDiv!: ElementRef;
1495
- // get viewChild to the app-mic
1496
- // @ViewChild(MicComponent) micComponent: MicComponent;
1810
+ this.isAdmin = true;
1497
1811
  this.isChatSettingsVisible = false;
1498
- this.chatInputControl = new FormControl();
1812
+ // chatInputControl moved to ChatFooterComponent
1499
1813
  this.isAIThinking = false;
1500
1814
  this.isUserTalking = false;
1501
1815
  this.aiIcon = `data:image/svg+xml;base64,${btoa(decodeURIComponent(ICONS.ai))}`;
@@ -1523,7 +1837,10 @@ class DCChatComponent {
1523
1837
  this.isInfoVisible = false;
1524
1838
  }
1525
1839
  async ngOnInit() {
1526
- this.defaultVoice = this.getVoice(this.agentCard.conversationSettings.voice);
1840
+ if (!this.agentCard?.conversationSettings) {
1841
+ throw new Error('Conversation settings are required');
1842
+ }
1843
+ this.defaultVoice = this.getVoice(this.agentCard?.conversationSettings?.voice);
1527
1844
  // This method overrides the conversationSettings of the agentCard so message will be there.
1528
1845
  this.conversationSettings = this.conversationBuilder.buildConversationSettings(this.agentCard, this.parseDict);
1529
1846
  this.agentCard.conversationSettings = this.conversationSettings; // Save a copy here.
@@ -1575,7 +1892,7 @@ class DCChatComponent {
1575
1892
  }
1576
1893
  return message;
1577
1894
  }
1578
- settingsClick() {
1895
+ changeUserChatSettings() {
1579
1896
  this.dialogService
1580
1897
  .open(DCConversationUserChatSettingsComponent, {
1581
1898
  width: '90vw',
@@ -1608,12 +1925,8 @@ class DCChatComponent {
1608
1925
  if (this.isAIThinking) {
1609
1926
  return;
1610
1927
  }
1611
- if (!message || !message.content) {
1612
- const text = this.chatInputControl.value;
1613
- if (!text) {
1614
- return;
1615
- }
1616
- message = { content: text, role: ChatRole.User };
1928
+ if (!message) {
1929
+ return;
1617
1930
  }
1618
1931
  this.messages.push(message); // This activates the render of the message
1619
1932
  // ESTO debe ser una especie de plugin.
@@ -1623,7 +1936,7 @@ class DCChatComponent {
1623
1936
  // this.messages.push({ content: res.text, role: ChatRole.AssistantHelper });
1624
1937
  // });
1625
1938
  // }
1626
- this.chatInputControl.setValue('');
1939
+ // Input clearing is now handled by the chat-footer component
1627
1940
  this.setAIthinking();
1628
1941
  try {
1629
1942
  // if (this.chatboxService.fakeConversations.length > 0) {
@@ -1725,7 +2038,8 @@ class DCChatComponent {
1725
2038
  }
1726
2039
  }
1727
2040
  setInputText(text) {
1728
- this.chatInputControl.setValue(text);
2041
+ // Method now receives text from the chat-footer component
2042
+ // No need to set the value as it's handled in the child component
1729
2043
  }
1730
2044
  async micFinished(eventBlob) {
1731
2045
  if (eventBlob instanceof Blob) {
@@ -1761,10 +2075,6 @@ class DCChatComponent {
1761
2075
  // this.speechService.speach(message.content);
1762
2076
  // }
1763
2077
  }
1764
- // public restartConversation() {
1765
- // this.messages = [];
1766
- // this.messages.length;
1767
- // }
1768
2078
  // public someTextSelected(event) {
1769
2079
  // console.log('someTextSelected', event);
1770
2080
  // this.wordMenuService.showMenu(event.position, event.text);
@@ -1799,24 +2109,9 @@ class DCChatComponent {
1799
2109
  console.log('chatUserSettings', this.chatUserSettings);
1800
2110
  this.isInfoVisible = true;
1801
2111
  }
1802
- async changeConversationCard() {
1803
- const response = prompt('¿Qué conversación quieres usar? Escribe el titulo ejemplo: word_reflection_level_1_base_es');
1804
- if (response) {
1805
- const filters = {
1806
- filters: {
1807
- title: { $regex: response },
1808
- },
1809
- };
1810
- console.log('filters', filters);
1811
- const conversationCards = await this.agentCardService.filterConversationCards(filters);
1812
- console.log('conversationCards', conversationCards);
1813
- this.alternativeConversation = conversationCards.rows;
1814
- }
1815
- }
1816
2112
  async restartConversation(conversation = null) {
1817
2113
  if (conversation) {
1818
2114
  this.agentCard = conversation;
1819
- // this.conversationSettings = this.promptBuilder.buildConversationSettings(this.conversationCard, this.parseDict);
1820
2115
  }
1821
2116
  await this.ngOnInit();
1822
2117
  }
@@ -1863,19 +2158,29 @@ and give the response in next JSON format.
1863
2158
  }
1864
2159
  console.log('response', response);
1865
2160
  }
1866
- 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$2.DialogService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1867
- 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: "<div style=\"display: flex; justify-content: space-between\">\n <span class=\"pointer\" (click)=\"restartConversation()\">Reiniciar conversaci\u00F3n</span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div style=\"font-size: large\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n</div>\n<div class=\"chat-container\" (touchmove)=\"$event.stopPropagation()\">\n <ul class=\"chat\">\n <ng-container *ngFor=\"let msn of messages\">\n <li *ngIf=\"msn.role == 'assistant'\" class=\"message left\">\n <img (click)=\"playMessage(msn)\" class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? imageUser\" />\n\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" appTextSelectable [chatMessage]=\"msn\"></dc-chat-message>\n </li>\n\n <li *ngIf=\"msn.role == 'assistantHelper'\" class=\"message-helper right\">\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" appTextSelectable [chatMessage]=\"msn\"></dc-chat-message>\n </li>\n\n <li *ngIf=\"msn.role == 'user'\" class=\"message right\">\n <img (click)=\"playMessage(msn)\" class=\"logo\" [src]=\"user?.urlPicture || imageUser\" alt=\"\" style=\"object-fit: cover; object-position: top\" />\n <dc-chat-message appTextSelectable [chatMessage]=\"msn\"></dc-chat-message>\n </li>\n </ng-container>\n\n @if (isAIThinking) {\n <li class=\"message left\">\n <img class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? aiIcon\" />\n\n <span style=\"font-size: 1.5em; position: absolute; left: 20px; bottom: -5px\">\uD83E\uDD14</span>\n\n <div class=\"flex\">\n <!-- <p-skeleton shape=\"circle\" size=\"4rem\" styleClass=\"mr-2\" /> -->\n <div class=\"self-center\" style=\"flex: 1\">\n <p-skeleton width=\"100%\" styleClass=\"mb-2\" />\n <p-skeleton width=\"75%\" />\n </div>\n </div>\n </li>\n }\n\n <li *ngIf=\"isUserTalking || isGettingTranscription\" class=\"message right\">\n <img class=\"logo fit-top-image\" [src]=\"user?.urlPicture || imageUser\" alt=\"\" />\n @if (isUserTalking) {\n <p> ... </p>\n } @if (isGettingTranscription) {\n <i class=\"pi pi-spin pi-spinner-dotted\"></i>\n }\n </li>\n </ul>\n\n <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 <textarea class=\"text-fix white-background\" [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n <button [disabled]=\"isAIThinking || !chatInputControl.value\" (click)=\"sendUserMessage()\" class=\"send-button\"> Enviar </button>\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n </div>\n</div>\n\n<p-dialog header=\"Informaci\u00F3n de la conversaci\u00F3n\" [(visible)]=\"isInfoVisible\" [modal]=\"true\">\n <p> <strong> Modelo: </strong> {{ this.chatUserSettings?.model?.provider }} - {{ this.chatUserSettings?.model?.modelName }} </p>\n\n <p>\n <strong> Tipo : </strong> {{ agentCard?.conversationSettings?.conversationType }} <strong> Text Engine: </strong>\n {{ agentCard?.conversationSettings?.textEngine }}\n </p>\n\n <p>\n <strong> Language: </strong> {{ agentCard?.lang }} <strong> TTS: </strong> {{ agentCard?.tts?.voice }} <strong> Secondary TTS: </strong>\n {{ agentCard?.tts?.secondaryVoice }}\n </p>\n\n <p> <strong> Parametros: </strong> {{ parseDict | json }} </p>\n <hr />\n <h5>Conversaci\u00F3n hasta ahora</h5>\n\n <div *ngFor=\"let message of conversationSettings.messages\">\n <p\n ><strong>{{ message.role }}: </strong> {{ message.content }}</p\n >\n </div>\n</p-dialog>\n", styles: [".top-5{margin-top:10px}.logo{width:30px;height:30px}.white-background{background-color:#fff}.selectable{z-index:1;position:fixed;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);background:transparent;border-radius:20px;display:block;padding:3px}*{box-sizing:border-box}.background1{background:url(/assets/background/default-background.webp);background-size:cover;background-position:center center;display:flex;align-items:center;justify-content:center;flex-direction:column;min-height:0px;overflow:auto;max-height:500px}h2{text-shadow:1px 1px 2px rgb(0,0,0);color:#fff;letter-spacing:1px;text-transform:uppercase;text-align:center}.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}.message-helper{background-color:#ffffffe6;box-shadow:0 7px 5px #00000080;position:relative;margin-bottom:30px}.message-helper.right{align-self:flex-end;padding:4px 45px 4px 15px}.message{background-color:#f6f6f6e6;border-radius:50px;box-shadow:7px 11px 14px #00000080;position:relative;margin-bottom:30px}.message .logo{position:absolute;top:50%;transform:translateY(-50%);border-radius:50%}.message .fit-top-image{object-fit:cover;object-position:top}.message.left{padding:10px 15px 10px 45px}.message.left .logo{left:5px}.message.right{align-self:flex-end;padding:10px 45px 10px 15px}.message.right .logo{right:5px}.input-container{margin-top:auto;display:flex;gap:10px;padding-top:15px;background:inherit}.input-container textarea{flex:1;padding:8px;border-radius:20px;border:1px solid #ccc;resize:none}.input-container .send-button{padding:8px 15px;border-radius:20px;border:none;background:#007bff;color:#fff;cursor:pointer}.input-container .send-button:disabled{background:#ccc;cursor:not-allowed}::ng-deep .cdk-overlay-container{z-index:1400}::ng-deep .cdk-global-overlay-wrapper{z-index:1400}::ng-deep .dialog-backdrop{background:#00000080;z-index:1299}\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: "pipe", type: i2.JsonPipe, name: "json" }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i6.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i6$1.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: i7$2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }] }); }
2161
+ 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 }); }
2162
+ 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$1.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$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i5$1.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"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "score", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }] }); }
1868
2163
  }
1869
2164
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCChatComponent, decorators: [{
1870
2165
  type: Component,
1871
- args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, FormsModule, ChatMessageComponent, ReactiveFormsModule, SkeletonModule, DCMicComponent, DialogModule, ProgressBarModule], providers: [DialogService], template: "<div style=\"display: flex; justify-content: space-between\">\n <span class=\"pointer\" (click)=\"restartConversation()\">Reiniciar conversaci\u00F3n</span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div style=\"font-size: large\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n</div>\n<div class=\"chat-container\" (touchmove)=\"$event.stopPropagation()\">\n <ul class=\"chat\">\n <ng-container *ngFor=\"let msn of messages\">\n <li *ngIf=\"msn.role == 'assistant'\" class=\"message left\">\n <img (click)=\"playMessage(msn)\" class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? imageUser\" />\n\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" appTextSelectable [chatMessage]=\"msn\"></dc-chat-message>\n </li>\n\n <li *ngIf=\"msn.role == 'assistantHelper'\" class=\"message-helper right\">\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" appTextSelectable [chatMessage]=\"msn\"></dc-chat-message>\n </li>\n\n <li *ngIf=\"msn.role == 'user'\" class=\"message right\">\n <img (click)=\"playMessage(msn)\" class=\"logo\" [src]=\"user?.urlPicture || imageUser\" alt=\"\" style=\"object-fit: cover; object-position: top\" />\n <dc-chat-message appTextSelectable [chatMessage]=\"msn\"></dc-chat-message>\n </li>\n </ng-container>\n\n @if (isAIThinking) {\n <li class=\"message left\">\n <img class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? aiIcon\" />\n\n <span style=\"font-size: 1.5em; position: absolute; left: 20px; bottom: -5px\">\uD83E\uDD14</span>\n\n <div class=\"flex\">\n <!-- <p-skeleton shape=\"circle\" size=\"4rem\" styleClass=\"mr-2\" /> -->\n <div class=\"self-center\" style=\"flex: 1\">\n <p-skeleton width=\"100%\" styleClass=\"mb-2\" />\n <p-skeleton width=\"75%\" />\n </div>\n </div>\n </li>\n }\n\n <li *ngIf=\"isUserTalking || isGettingTranscription\" class=\"message right\">\n <img class=\"logo fit-top-image\" [src]=\"user?.urlPicture || imageUser\" alt=\"\" />\n @if (isUserTalking) {\n <p> ... </p>\n } @if (isGettingTranscription) {\n <i class=\"pi pi-spin pi-spinner-dotted\"></i>\n }\n </li>\n </ul>\n\n <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 <textarea class=\"text-fix white-background\" [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n <button [disabled]=\"isAIThinking || !chatInputControl.value\" (click)=\"sendUserMessage()\" class=\"send-button\"> Enviar </button>\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n </div>\n</div>\n\n<p-dialog header=\"Informaci\u00F3n de la conversaci\u00F3n\" [(visible)]=\"isInfoVisible\" [modal]=\"true\">\n <p> <strong> Modelo: </strong> {{ this.chatUserSettings?.model?.provider }} - {{ this.chatUserSettings?.model?.modelName }} </p>\n\n <p>\n <strong> Tipo : </strong> {{ agentCard?.conversationSettings?.conversationType }} <strong> Text Engine: </strong>\n {{ agentCard?.conversationSettings?.textEngine }}\n </p>\n\n <p>\n <strong> Language: </strong> {{ agentCard?.lang }} <strong> TTS: </strong> {{ agentCard?.tts?.voice }} <strong> Secondary TTS: </strong>\n {{ agentCard?.tts?.secondaryVoice }}\n </p>\n\n <p> <strong> Parametros: </strong> {{ parseDict | json }} </p>\n <hr />\n <h5>Conversaci\u00F3n hasta ahora</h5>\n\n <div *ngFor=\"let message of conversationSettings.messages\">\n <p\n ><strong>{{ message.role }}: </strong> {{ message.content }}</p\n >\n </div>\n</p-dialog>\n", styles: [".top-5{margin-top:10px}.logo{width:30px;height:30px}.white-background{background-color:#fff}.selectable{z-index:1;position:fixed;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);background:transparent;border-radius:20px;display:block;padding:3px}*{box-sizing:border-box}.background1{background:url(/assets/background/default-background.webp);background-size:cover;background-position:center center;display:flex;align-items:center;justify-content:center;flex-direction:column;min-height:0px;overflow:auto;max-height:500px}h2{text-shadow:1px 1px 2px rgb(0,0,0);color:#fff;letter-spacing:1px;text-transform:uppercase;text-align:center}.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}.message-helper{background-color:#ffffffe6;box-shadow:0 7px 5px #00000080;position:relative;margin-bottom:30px}.message-helper.right{align-self:flex-end;padding:4px 45px 4px 15px}.message{background-color:#f6f6f6e6;border-radius:50px;box-shadow:7px 11px 14px #00000080;position:relative;margin-bottom:30px}.message .logo{position:absolute;top:50%;transform:translateY(-50%);border-radius:50%}.message .fit-top-image{object-fit:cover;object-position:top}.message.left{padding:10px 15px 10px 45px}.message.left .logo{left:5px}.message.right{align-self:flex-end;padding:10px 45px 10px 15px}.message.right .logo{right:5px}.input-container{margin-top:auto;display:flex;gap:10px;padding-top:15px;background:inherit}.input-container textarea{flex:1;padding:8px;border-radius:20px;border:1px solid #ccc;resize:none}.input-container .send-button{padding:8px 15px;border-radius:20px;border:none;background:#007bff;color:#fff;cursor:pointer}.input-container .send-button:disabled{background:#ccc;cursor:not-allowed}::ng-deep .cdk-overlay-container{z-index:1400}::ng-deep .cdk-global-overlay-wrapper{z-index:1400}::ng-deep .dialog-backdrop{background:#00000080;z-index:1299}\n"] }]
2166
+ args: [{ selector: 'dc-chat', standalone: true, imports: [
2167
+ CommonModule,
2168
+ FormsModule,
2169
+ ChatMessageComponent,
2170
+ ReactiveFormsModule,
2171
+ SkeletonModule,
2172
+ DialogModule,
2173
+ ProgressBarModule,
2174
+ ChatHeaderComponent,
2175
+ ChatFooterComponent,
2176
+ ], 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"] }]
1872
2177
  }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
1873
2178
  type: Inject,
1874
2179
  args: [CONVERSATION_AI_TOKEN]
1875
- }] }, { type: i9.ToastAlertsAbstractService, decorators: [{
2180
+ }] }, { type: i6.ToastAlertsAbstractService, decorators: [{
1876
2181
  type: Inject,
1877
2182
  args: [TOAST_ALERTS_TOKEN]
1878
- }] }, { type: DCConversationPromptBuilderService }, { type: i1$2.DialogService }, { type: i0.ChangeDetectorRef }], propDecorators: { chatUserSettings: [{
2183
+ }] }, { type: DCConversationPromptBuilderService }, { type: i1$3.DialogService }, { type: i0.ChangeDetectorRef }], propDecorators: { chatUserSettings: [{
1879
2184
  type: Input
1880
2185
  }], agentCard: [{
1881
2186
  type: Input
@@ -2026,7 +2331,7 @@ class PromptPreviewDialogComponent {
2026
2331
  close() {
2027
2332
  this.dialogRef.close();
2028
2333
  }
2029
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, deps: [{ token: i1$2.DynamicDialogConfig }, { token: i1$2.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
2334
+ 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 }); }
2030
2335
  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 }] }); }
2031
2336
  }
2032
2337
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, decorators: [{
@@ -2037,7 +2342,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2037
2342
  imports: [CommonModule, DialogModule],
2038
2343
  template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `,
2039
2344
  }]
2040
- }], ctorParameters: () => [{ type: i1$2.DynamicDialogConfig }, { type: i1$2.DynamicDialogRef }] });
2345
+ }], ctorParameters: () => [{ type: i1$3.DynamicDialogConfig }, { type: i1$3.DynamicDialogRef }] });
2041
2346
 
2042
2347
  class TranslateDialogComponent {
2043
2348
  constructor(fb, dialogRef, data) {
@@ -2059,7 +2364,7 @@ class TranslateDialogComponent {
2059
2364
  onConfirm() {
2060
2365
  this.dialogRef.close(this.form.value.targetLang);
2061
2366
  }
2062
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, deps: [{ token: i1$1.FormBuilder }, { token: i2$1.DialogRef }, { token: DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
2367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, deps: [{ token: i1$2.FormBuilder }, { token: i2$1.DialogRef }, { token: DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
2063
2368
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TranslateDialogComponent, isStandalone: true, selector: "dc-translate-dialog", ngImport: i0, template: `
2064
2369
  <div class="translate-dialog">
2065
2370
  <h4>Tu idioma actual es: {{ data.currentLang }}</h4>
@@ -2077,7 +2382,7 @@ class TranslateDialogComponent {
2077
2382
  <button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
2078
2383
  </div>
2079
2384
  </div>
2080
- `, 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: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
2385
+ `, 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$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
2081
2386
  }
2082
2387
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, decorators: [{
2083
2388
  type: Component,
@@ -2099,7 +2404,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2099
2404
  </div>
2100
2405
  </div>
2101
2406
  `, 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"] }]
2102
- }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: i2$1.DialogRef }, { type: undefined, decorators: [{
2407
+ }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: i2$1.DialogRef }, { type: undefined, decorators: [{
2103
2408
  type: Inject,
2104
2409
  args: [DIALOG_DATA]
2105
2410
  }] }] });
@@ -2164,13 +2469,13 @@ class AccountPlatformForm {
2164
2469
  this.toastService.warn(infoToast);
2165
2470
  }
2166
2471
  }
2167
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, deps: [{ token: i1$3.ActivatedRoute }, { token: i1$1.FormBuilder }, { token: i1$3.Router }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
2168
- 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$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.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.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i4$2.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2472
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, deps: [{ token: i1$4.ActivatedRoute }, { token: i1$2.FormBuilder }, { token: i1$4.Router }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
2473
+ 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$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.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$2.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2169
2474
  }
2170
2475
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, decorators: [{
2171
2476
  type: Component,
2172
2477
  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"] }]
2173
- }], ctorParameters: () => [{ type: i1$3.ActivatedRoute }, { type: i1$1.FormBuilder }, { type: i1$3.Router }, { type: i9.ToastAlertsAbstractService, decorators: [{
2478
+ }], ctorParameters: () => [{ type: i1$4.ActivatedRoute }, { type: i1$2.FormBuilder }, { type: i1$4.Router }, { type: i6.ToastAlertsAbstractService, decorators: [{
2174
2479
  type: Inject,
2175
2480
  args: [TOAST_ALERTS_TOKEN]
2176
2481
  }] }], propDecorators: { formArray: [{
@@ -2204,6 +2509,7 @@ class DCAgentCardFormComponent {
2204
2509
  this.accountsOptions = Object.values(EAccountsPlatform);
2205
2510
  this.languageOptions = Object.entries(LangCodeDescriptionEs).map(([value, label]) => ({ value, label }));
2206
2511
  this.agentCardId = this.activatedRoute.snapshot.paramMap.get('id');
2512
+ this.audioSpeedOptions = Object.values(AudioSpeed$1).map((speed) => ({ label: speed, value: speed }));
2207
2513
  this.storageSettings = this.getSettings();
2208
2514
  this.bannerImgSettings = {
2209
2515
  path: 'conversation-cards/' + this.agentCardId,
@@ -2248,9 +2554,9 @@ class DCAgentCardFormComponent {
2248
2554
  textEngine: [TextEngines.SimpleText],
2249
2555
  conversationType: [ConversationType.General],
2250
2556
  autoStart: [true],
2557
+ tts: this.fb.group({ voice: [''], secondaryVoice: [''], speed: ['1.0'], speedRate: [1.0] }),
2251
2558
  }),
2252
2559
  lang: [''],
2253
- tts: this.fb.group({ voice: [''], secondaryVoice: [''], speed: ['1.0'], speedRate: [1.0] }),
2254
2560
  metaApp: this.fb.group({ isPublished: [false], isPublic: [false], authorId: [''], authorEmail: [''], takenCount: [0] }),
2255
2561
  model: this.fb.group({ id: [''], modelName: [''], provider: [''] }),
2256
2562
  accounts: this.fb.array([]),
@@ -2593,8 +2899,8 @@ class DCAgentCardFormComponent {
2593
2899
  this.toastService.success({ title: 'Sticker removed', subtitle: 'Sticker was removed' });
2594
2900
  this.cdr.detectChanges();
2595
2901
  }
2596
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, deps: [{ token: i1$1.FormBuilder }, { token: i2$2.MultiImagesStorageService }, { token: CONVERSATION_AI_TOKEN }, { token: i0.ChangeDetectorRef }, { token: i1$3.Router }, { token: i1$3.ActivatedRoute }, { token: i1$2.DialogService }, { token: DCConversationPromptBuilderService }, { token: TOAST_ALERTS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
2597
- 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)\">&#x2716;</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"] }] }); }
2902
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, deps: [{ token: i1$2.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 }); }
2903
+ 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\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)\">&#x2716;</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$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.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$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.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$2.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"] }] }); }
2598
2904
  }
2599
2905
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
2600
2906
  type: Component,
@@ -2619,11 +2925,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2619
2925
  PopoverModule,
2620
2926
  ProviderSelectorComponent,
2621
2927
  AccountPlatformForm,
2622
- ], 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)\">&#x2716;</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"] }]
2623
- }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: i2$2.MultiImagesStorageService }, { type: AgentCardsAbstractService, decorators: [{
2928
+ ], 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)\">&#x2716;</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"] }]
2929
+ }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: i2$2.MultiImagesStorageService }, { type: AgentCardsAbstractService, decorators: [{
2624
2930
  type: Inject,
2625
2931
  args: [CONVERSATION_AI_TOKEN]
2626
- }] }, { type: i0.ChangeDetectorRef }, { type: i1$3.Router }, { type: i1$3.ActivatedRoute }, { type: i1$2.DialogService }, { type: DCConversationPromptBuilderService }, { type: i9.ToastAlertsAbstractService, decorators: [{
2932
+ }] }, { type: i0.ChangeDetectorRef }, { type: i1$4.Router }, { type: i1$4.ActivatedRoute }, { type: i1$3.DialogService }, { type: DCConversationPromptBuilderService }, { type: i6.ToastAlertsAbstractService, decorators: [{
2627
2933
  type: Optional
2628
2934
  }, {
2629
2935
  type: Inject,
@@ -2646,9 +2952,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2646
2952
 
2647
2953
  class DCConversationCardUIComponent {
2648
2954
  constructor() {
2649
- this.onDetailsCard = new EventEmitter();
2650
- this.onEditCard = new EventEmitter();
2651
- this.onDeleteCard = new EventEmitter();
2955
+ this.speedDialModel = [
2956
+ { label: 'Edit', icon: 'pi pi-pencil', title: 'edit', severity: 'primary', command: () => this.onEdit() },
2957
+ { label: 'Delete', icon: 'pi pi-trash', title: 'delete', severity: 'danger', command: () => this.onDelete() },
2958
+ { label: 'Select', icon: 'pi pi-check', title: 'select', severity: 'success', command: () => this.onDetails() },
2959
+ ];
2960
+ this.showOptions = true;
2961
+ this.onCardAction = new EventEmitter();
2652
2962
  }
2653
2963
  ngOnInit() {
2654
2964
  const name = this.card.characterCard.data.name;
@@ -2656,27 +2966,25 @@ class DCConversationCardUIComponent {
2656
2966
  this.card.characterCard.data.description = description.replace(/{{char}}/g, name);
2657
2967
  }
2658
2968
  onDetails() {
2659
- this.onDetailsCard.emit(this.card._id);
2969
+ this.onCardAction.emit({ event: 'details', card: this.card });
2660
2970
  }
2661
2971
  onEdit() {
2662
- this.onEditCard.emit(this.card._id);
2972
+ this.onCardAction.emit({ event: 'edit', card: this.card });
2663
2973
  }
2664
2974
  onDelete() {
2665
- this.onDeleteCard.emit(this.card._id);
2975
+ this.onCardAction.emit({ event: 'delete', card: this.card });
2666
2976
  }
2667
2977
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2668
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: "card" }, outputs: { onDetailsCard: "onDetailsCard", onEditCard: "onEditCard", onDeleteCard: "onDeleteCard" }, ngImport: i0, template: "<p-card class=\"card-image\">\n <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div 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\n<!-- <div class=\"card\">\n \n\n <span class=\"title-text\">{{ card.title }}</span>\n\n <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n</div> -->\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;border-radius:15px}.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$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2978
+ 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 }); }
2669
2979
  }
2670
2980
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
2671
2981
  type: Component,
2672
- args: [{ selector: 'dc-agent-card-default-ui', imports: [PopoverModule, TruncatePipe, ButtonModule, SpeedDialModule, CardModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p-card class=\"card-image\">\n <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div 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\n<!-- <div class=\"card\">\n \n\n <span class=\"title-text\">{{ card.title }}</span>\n\n <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n</div> -->\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;border-radius:15px}.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"] }]
2982
+ 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"] }]
2673
2983
  }], propDecorators: { card: [{
2674
2984
  type: Input
2675
- }], onDetailsCard: [{
2676
- type: Output
2677
- }], onEditCard: [{
2678
- type: Output
2679
- }], onDeleteCard: [{
2985
+ }], showOptions: [{
2986
+ type: Input
2987
+ }], onCardAction: [{
2680
2988
  type: Output
2681
2989
  }] } });
2682
2990
 
@@ -2688,11 +2996,6 @@ const DefaultColumns = [
2688
2996
  { field: 'characterCard.data.name', header: 'Character Name', type: 'text' },
2689
2997
  { field: 'title', header: 'Title', type: 'text' },
2690
2998
  ];
2691
- const DefaultActions = [
2692
- { label: 'Edit', icon: 'pi pi-pencil', title: 'edit', severity: 'primary' },
2693
- { label: 'Delete', icon: 'pi pi-trash', title: 'delete', severity: 'danger' },
2694
- { label: 'Select', icon: 'pi pi-check', title: 'select', severity: 'success' },
2695
- ];
2696
2999
  class AgentCardListComponent extends PaginationBase {
2697
3000
  constructor(agentCardService, toastService, route, router, cdr) {
2698
3001
  super(route, router);
@@ -2700,16 +3003,8 @@ class AgentCardListComponent extends PaginationBase {
2700
3003
  this.toastService = toastService;
2701
3004
  this.cdr = cdr;
2702
3005
  this.viewMode = 'cards';
3006
+ this.showOptions = true;
2703
3007
  this.gridLayout = true;
2704
- // @deprecated use onAction
2705
- this.goToDetailsEvent = new EventEmitter();
2706
- // @deprecated use onAction
2707
- this.goToEditEvent = new EventEmitter();
2708
- // @deprecated use onAction
2709
- this.deleteEvent = new EventEmitter();
2710
- // @deprecated use onAction
2711
- this.onSelect = new EventEmitter();
2712
- this.actions = DefaultActions;
2713
3008
  this.agentCards = [];
2714
3009
  this.cardEventSubs = [];
2715
3010
  this.cardComponent = null;
@@ -2729,12 +3024,8 @@ class AgentCardListComponent extends PaginationBase {
2729
3024
  subscribeToCardEvents() {
2730
3025
  this.outlets.forEach((outlet) => {
2731
3026
  const instance = outlet.componentInstance;
2732
- this.cardEventSubs.push(instance.onDetailsCard.subscribe((id) => {
2733
- this.goToDetails(id);
2734
- }), instance.onEditCard.subscribe((id) => {
2735
- this.goToEdit(id);
2736
- }), instance.onDeleteCard.subscribe((id) => {
2737
- this.deleteCard(id);
3027
+ this.cardEventSubs.push(instance.onCardAction.subscribe((action) => {
3028
+ this.onCardAction({ action: action.event, item: action.card });
2738
3029
  }));
2739
3030
  });
2740
3031
  }
@@ -2768,23 +3059,34 @@ class AgentCardListComponent extends PaginationBase {
2768
3059
  async getAllConversationCards() {
2769
3060
  return this.agentCardService.getAllConversationCards();
2770
3061
  }
2771
- async goToDetails(id) {
2772
- this.goToDetailsEvent.emit(id);
2773
- }
2774
- async goToEdit(id = null) {
2775
- console.log('goToEdit', id);
2776
- this.goToEditEvent.emit(id);
3062
+ async goToDetails(action) {
3063
+ this.onAction.emit({ action: 'details', item: action.item });
2777
3064
  }
2778
3065
  async deleteCard(id) {
2779
3066
  await this.agentCardService.deleteConversationCard(id);
2780
3067
  this.agentCards = this.agentCards.filter((card) => card._id !== id);
2781
- this.deleteEvent.emit(id);
2782
3068
  this.toastService.success({ title: 'Conversation card deleted', subtitle: 'The conversation card has been deleted' });
3069
+ this.cdr.detectChanges();
2783
3070
  }
2784
3071
  // Implement the abstract method from PaginationBase
2785
3072
  async loadData() {
2786
3073
  return this.loadConversationCards();
2787
3074
  }
3075
+ doFilterBarAction({ action, item }) {
3076
+ // console.log('doAction', { action, item });
3077
+ if (action === 'new') {
3078
+ this.onAction.emit({ action: 'new' });
3079
+ }
3080
+ else if (action === 'edit') {
3081
+ this.onAction.emit({ action: 'edit', item: item?.['id'] });
3082
+ }
3083
+ else if (action === 'delete') {
3084
+ this.onAction.emit({ action: 'delete', item: item?.['id'] });
3085
+ }
3086
+ else if (action === 'changeView') {
3087
+ this.viewMode = this.viewMode === 'cards' ? 'table' : 'cards';
3088
+ }
3089
+ }
2788
3090
  async search(text) {
2789
3091
  console.log('search', text);
2790
3092
  const filters = {
@@ -2807,24 +3109,19 @@ class AgentCardListComponent extends PaginationBase {
2807
3109
  this.cdr.detectChanges();
2808
3110
  this.subscribeDinamicInstantToEvents();
2809
3111
  }
2810
- doBarAction({ action, item }) {
2811
- debugger;
2812
- // console.log('doAction', { action, item });
2813
- if (action === 'new') {
2814
- this.goToEdit();
3112
+ onCardAction(actionEvent) {
3113
+ if (actionEvent.action == 'delete') {
3114
+ this.deleteCard(actionEvent.item._id);
2815
3115
  }
2816
- else if (action === 'edit') {
2817
- this.goToEdit(item?.['id']);
2818
- }
2819
- else if (action === 'delete') {
2820
- this.deleteCard(item?.['id']);
3116
+ else if (actionEvent.action == 'edit') {
3117
+ this.doAction(actionEvent); // handle by father
2821
3118
  }
2822
- else if (action === 'changeView') {
2823
- this.viewMode = this.viewMode === 'cards' ? 'table' : 'cards';
3119
+ else if (actionEvent.action == 'details') {
3120
+ this.doAction(actionEvent); // handle by father.
2824
3121
  }
2825
3122
  }
2826
- 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$3.ActivatedRoute }, { token: i1$3.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2827
- 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", actions: "actions" }, outputs: { goToDetailsEvent: "goToDetailsEvent", goToEditEvent: "goToEditEvent", deleteEvent: "deleteEvent", onSelect: "onSelect" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onAction)=\"doBarAction($event)\" (onSearch)=\"search($event)\" (onFilterChange)=\"filterChange($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions\" (onAction)=\"doAction($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 }\"> </ng-container>\n\n @if(getCustomButtons) {\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(card)\"\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 </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: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.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$2.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: ["onAction", "onChangeSort", "onNew", "onSearch", "onFilterChange"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i6.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i5$2.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: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }] }); }
3123
+ 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 }); }
3124
+ 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$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.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$1.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"] }] }); }
2828
3125
  }
2829
3126
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AgentCardListComponent, decorators: [{
2830
3127
  type: Component,
@@ -2838,69 +3135,99 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2838
3135
  SkeletonModule,
2839
3136
  SpeedDialModule,
2840
3137
  QuickTableComponent,
2841
- ], standalone: true, template: "<dc-filter-bar [isAdmin]=\"true\" (onAction)=\"doBarAction($event)\" (onSearch)=\"search($event)\" (onFilterChange)=\"filterChange($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions\" (onAction)=\"doAction($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 }\"> </ng-container>\n\n @if(getCustomButtons) {\n <div style=\"position: absolute; top: 4px; right: 4px; z-index: 1000\">\n <p-speeddial\n [model]=\"getCustomButtons(card)\"\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 </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"] }]
3138
+ ], 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"] }]
2842
3139
  }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
2843
3140
  type: Inject,
2844
3141
  args: [CONVERSATION_AI_TOKEN]
2845
- }] }, { type: i9.ToastAlertsAbstractService, decorators: [{
3142
+ }] }, { type: i6.ToastAlertsAbstractService, decorators: [{
2846
3143
  type: Inject,
2847
3144
  args: [TOAST_ALERTS_TOKEN]
2848
- }] }, { type: i1$3.ActivatedRoute }, { type: i1$3.Router }, { type: i0.ChangeDetectorRef }], propDecorators: { viewMode: [{
3145
+ }] }, { type: i1$4.ActivatedRoute }, { type: i1$4.Router }, { type: i0.ChangeDetectorRef }], propDecorators: { viewMode: [{
2849
3146
  type: Input
2850
3147
  }], customCardComponent: [{
2851
3148
  type: Input
3149
+ }], showOptions: [{
3150
+ type: Input
2852
3151
  }], gridLayout: [{
2853
3152
  type: Input
2854
3153
  }], getCustomButtons: [{
2855
3154
  type: Input
2856
- }], goToDetailsEvent: [{
2857
- type: Output
2858
- }], goToEditEvent: [{
2859
- type: Output
2860
- }], deleteEvent: [{
2861
- type: Output
2862
- }], onSelect: [{
2863
- type: Output
2864
3155
  }], outlets: [{
2865
3156
  type: ViewChildren,
2866
3157
  args: ['outlet']
2867
- }], actions: [{
2868
- type: Input
2869
3158
  }] } });
2870
3159
 
3160
+ class ParseCardPipe {
3161
+ constructor() {
3162
+ this.userDataExchange = inject(USER_DATA_EXCHANGE);
3163
+ this.builderConversation = inject(DCConversationPromptBuilderService);
3164
+ }
3165
+ transform(text, card = null) {
3166
+ let parseDict = this.userDataExchange.getParseDict();
3167
+ if (card) {
3168
+ const cardMacros = { char: card?.characterCard?.data?.name };
3169
+ parseDict = { ...parseDict, ...cardMacros };
3170
+ }
3171
+ const result = this.builderConversation.applyReplacements(text, parseDict);
3172
+ return result;
3173
+ }
3174
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3175
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, isStandalone: true, name: "parseCard" }); }
3176
+ }
3177
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, decorators: [{
3178
+ type: Pipe,
3179
+ args: [{
3180
+ name: 'parseCard',
3181
+ standalone: true,
3182
+ }]
3183
+ }] });
3184
+
2871
3185
  class DcAgentCardDetailsComponent {
2872
- constructor(agentCardService, route, cdr) {
3186
+ constructor(agentCardService, route, cdr, userDataExchange) {
2873
3187
  this.agentCardService = agentCardService;
2874
3188
  this.route = route;
2875
3189
  this.cdr = cdr;
2876
- this.conversationCardId = '';
3190
+ this.userDataExchange = userDataExchange;
3191
+ this.agentCardId = '';
2877
3192
  this.onStartConversation = new EventEmitter();
3193
+ this.showInfoLayer = false;
2878
3194
  }
2879
3195
  async ngOnInit() {
2880
- console.log('DcAgentCardDetailsComponent', this.conversationCardId);
3196
+ console.log('DcAgentCardDetailsComponent', this.agentCardId);
2881
3197
  const id = this.route.snapshot.paramMap.get('id');
2882
3198
  if (id) {
2883
- this.conversationCardId = id;
2884
- console.log(this.conversationCardId);
3199
+ this.agentCardId = id;
3200
+ console.log(this.agentCardId);
2885
3201
  }
2886
- this.conversation = await this.agentCardService.findConversationCardByID(this.conversationCardId);
2887
- console.log(this.conversation);
3202
+ this.agentCard = await this.agentCardService.findConversationCardByID(this.agentCardId);
3203
+ if (!this.agentCard.conversationSettings) {
3204
+ console.warn('⚠️ Conversation settings not found ⚠️ probably is an old version of the card.');
3205
+ this.agentCard.conversationSettings = {};
3206
+ }
3207
+ console.log(this.agentCard);
2888
3208
  this.cdr.detectChanges();
2889
3209
  }
2890
3210
  startConversation() {
2891
- console.log('last version startConversation', this.conversation);
2892
- this.onStartConversation.emit(this.conversation);
3211
+ console.log('⚠️ last version startConversation', this.agentCard);
3212
+ this.onStartConversation.emit(this.agentCard);
3213
+ }
3214
+ toggleInfoLayer() {
3215
+ this.showInfoLayer = !this.showInfoLayer;
3216
+ this.cdr.markForCheck();
2893
3217
  }
2894
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: i1$3.ActivatedRoute }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2895
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { conversationCardId: "conversationCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ conversation?.title }}</h2>\n <span class=\"version\">v{{ conversation?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"conversation?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ conversation?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of conversation?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"conversation?.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>{{ conversation?.characterCard.data?.description }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"conversation?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ conversation?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"conversation?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ conversation?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"conversation?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of conversation?.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\">{{ conversation?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ conversation?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ conversation?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"conversation?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ conversation?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ conversation?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ conversation?.tts.speed }} ({{ conversation?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".dc-conversation-card-details{padding:1.5rem;max-width:800px;margin:0 auto}.dc-conversation-card-details .header{display:flex;align-items:center;margin-bottom:2rem;gap:1rem}.dc-conversation-card-details .header h2{margin:0;font-size:1.8rem;color:#333}.dc-conversation-card-details .header .version{color:#666;font-size:.9rem;padding:.2rem .5rem;background:#f5f5f5;border-radius:4px}.dc-conversation-card-details .character-card{background:#fff;border-radius:8px;padding:1.5rem;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.dc-conversation-card-details .character-card .character-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.dc-conversation-card-details .character-card .character-header h3{margin:0;color:#2c3e50;font-size:1.4rem}.dc-conversation-card-details .character-card .character-header .tags{display:flex;gap:.5rem;flex-wrap:wrap}.dc-conversation-card-details .character-card .character-header .tags .tag{background:#e9ecef;color:#495057;padding:.2rem .6rem;border-radius:4px;font-size:.8rem}.dc-conversation-card-details .character-card .description{color:#555;line-height:1.6;margin-bottom:1.5rem}.dc-conversation-card-details .character-card h4{color:#2c3e50;margin:1rem 0 .5rem;font-size:1.1rem}.dc-conversation-card-details .character-card .scenario,.dc-conversation-card-details .character-card .first-message{background:#f8f9fa;padding:1rem;border-radius:6px;margin-bottom:1rem}.dc-conversation-card-details .character-card .scenario p,.dc-conversation-card-details .character-card .first-message p{margin:0;color:#666}.dc-conversation-card-details .character-card .alternate-greetings ul{list-style:none;padding:0;margin:0}.dc-conversation-card-details .character-card .alternate-greetings ul li{padding:.5rem;border-bottom:1px solid #eee;color:#666}.dc-conversation-card-details .character-card .alternate-greetings ul li:last-child{border-bottom:none}.dc-conversation-card-details .settings{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}@media (max-width: 768px){.dc-conversation-card-details .settings{grid-template-columns:1fr}}.dc-conversation-card-details .settings .conversation-settings,.dc-conversation-card-details .settings .tts-settings{background:#fff;border-radius:8px;padding:1.5rem;box-shadow:0 2px 4px #0000001a}.dc-conversation-card-details .settings .conversation-settings h3,.dc-conversation-card-details .settings .tts-settings h3{margin:0 0 1rem;color:#2c3e50;font-size:1.2rem}.dc-conversation-card-details .settings .conversation-settings .setting-item,.dc-conversation-card-details .settings .tts-settings .setting-item{display:flex;justify-content:space-between;padding:.5rem 0;border-bottom:1px solid #eee}.dc-conversation-card-details .settings .conversation-settings .setting-item:last-child,.dc-conversation-card-details .settings .tts-settings .setting-item:last-child{border-bottom:none}.dc-conversation-card-details .settings .conversation-settings .setting-item .label,.dc-conversation-card-details .settings .tts-settings .setting-item .label{color:#666;font-weight:500}.dc-conversation-card-details .settings .conversation-settings .setting-item .value,.dc-conversation-card-details .settings .tts-settings .setting-item .value{color:#333}.btn{padding:.5rem 1rem;background:#007bff;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s ease-in-out}.btn:hover{background:#0056b3}.image-wrapper{display:flex;justify-content:center;width:100%}.image-wrapper .image{position:relative;max-width:400px;width:100%}.image-wrapper .image img{width:100%;height:auto;object-fit:cover;border-radius:8px}.image-wrapper .image .actions{position:absolute;bottom:20px;left:50%;transform:translate(-50%);z-index:2}.image-wrapper .image .actions .btn{min-width:200px;background:#007bffe6;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.image-wrapper .image .actions .btn:hover{background:#0056b3f2}.actions-wrapper{display:flex;justify-content:center;width:100%;margin:1rem 0}.actions-wrapper .actions .btn{min-width:200px}\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: ButtonModule }, { 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"] }] }); }
3218
+ 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 }); }
3219
+ 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$1.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" }] }); }
2896
3220
  }
2897
3221
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
2898
3222
  type: Component,
2899
- args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [CommonModule, ButtonModule], template: "<div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ conversation?.title }}</h2>\n <span class=\"version\">v{{ conversation?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"conversation?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ conversation?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of conversation?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"conversation?.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>{{ conversation?.characterCard.data?.description }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"conversation?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ conversation?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"conversation?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ conversation?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"conversation?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of conversation?.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\">{{ conversation?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ conversation?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ conversation?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"conversation?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ conversation?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ conversation?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ conversation?.tts.speed }} ({{ conversation?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".dc-conversation-card-details{padding:1.5rem;max-width:800px;margin:0 auto}.dc-conversation-card-details .header{display:flex;align-items:center;margin-bottom:2rem;gap:1rem}.dc-conversation-card-details .header h2{margin:0;font-size:1.8rem;color:#333}.dc-conversation-card-details .header .version{color:#666;font-size:.9rem;padding:.2rem .5rem;background:#f5f5f5;border-radius:4px}.dc-conversation-card-details .character-card{background:#fff;border-radius:8px;padding:1.5rem;margin-bottom:2rem;box-shadow:0 2px 4px #0000001a}.dc-conversation-card-details .character-card .character-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.dc-conversation-card-details .character-card .character-header h3{margin:0;color:#2c3e50;font-size:1.4rem}.dc-conversation-card-details .character-card .character-header .tags{display:flex;gap:.5rem;flex-wrap:wrap}.dc-conversation-card-details .character-card .character-header .tags .tag{background:#e9ecef;color:#495057;padding:.2rem .6rem;border-radius:4px;font-size:.8rem}.dc-conversation-card-details .character-card .description{color:#555;line-height:1.6;margin-bottom:1.5rem}.dc-conversation-card-details .character-card h4{color:#2c3e50;margin:1rem 0 .5rem;font-size:1.1rem}.dc-conversation-card-details .character-card .scenario,.dc-conversation-card-details .character-card .first-message{background:#f8f9fa;padding:1rem;border-radius:6px;margin-bottom:1rem}.dc-conversation-card-details .character-card .scenario p,.dc-conversation-card-details .character-card .first-message p{margin:0;color:#666}.dc-conversation-card-details .character-card .alternate-greetings ul{list-style:none;padding:0;margin:0}.dc-conversation-card-details .character-card .alternate-greetings ul li{padding:.5rem;border-bottom:1px solid #eee;color:#666}.dc-conversation-card-details .character-card .alternate-greetings ul li:last-child{border-bottom:none}.dc-conversation-card-details .settings{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}@media (max-width: 768px){.dc-conversation-card-details .settings{grid-template-columns:1fr}}.dc-conversation-card-details .settings .conversation-settings,.dc-conversation-card-details .settings .tts-settings{background:#fff;border-radius:8px;padding:1.5rem;box-shadow:0 2px 4px #0000001a}.dc-conversation-card-details .settings .conversation-settings h3,.dc-conversation-card-details .settings .tts-settings h3{margin:0 0 1rem;color:#2c3e50;font-size:1.2rem}.dc-conversation-card-details .settings .conversation-settings .setting-item,.dc-conversation-card-details .settings .tts-settings .setting-item{display:flex;justify-content:space-between;padding:.5rem 0;border-bottom:1px solid #eee}.dc-conversation-card-details .settings .conversation-settings .setting-item:last-child,.dc-conversation-card-details .settings .tts-settings .setting-item:last-child{border-bottom:none}.dc-conversation-card-details .settings .conversation-settings .setting-item .label,.dc-conversation-card-details .settings .tts-settings .setting-item .label{color:#666;font-weight:500}.dc-conversation-card-details .settings .conversation-settings .setting-item .value,.dc-conversation-card-details .settings .tts-settings .setting-item .value{color:#333}.btn{padding:.5rem 1rem;background:#007bff;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s ease-in-out}.btn:hover{background:#0056b3}.image-wrapper{display:flex;justify-content:center;width:100%}.image-wrapper .image{position:relative;max-width:400px;width:100%}.image-wrapper .image img{width:100%;height:auto;object-fit:cover;border-radius:8px}.image-wrapper .image .actions{position:absolute;bottom:20px;left:50%;transform:translate(-50%);z-index:2}.image-wrapper .image .actions .btn{min-width:200px;background:#007bffe6;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.image-wrapper .image .actions .btn:hover{background:#0056b3f2}.actions-wrapper{display:flex;justify-content:center;width:100%;margin:1rem 0}.actions-wrapper .actions .btn{min-width:200px}\n"] }]
3223
+ 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"] }]
2900
3224
  }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
2901
3225
  type: Inject,
2902
3226
  args: [CONVERSATION_AI_TOKEN]
2903
- }] }, { type: i1$3.ActivatedRoute }, { type: i0.ChangeDetectorRef }], propDecorators: { conversationCardId: [{
3227
+ }] }, { type: i1$4.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: UserDataExchangeAbstractService, decorators: [{
3228
+ type: Inject,
3229
+ args: [USER_DATA_EXCHANGE]
3230
+ }] }], propDecorators: { agentCardId: [{
2904
3231
  type: Input
2905
3232
  }], onStartConversation: [{
2906
3233
  type: Output
@@ -2915,5 +3242,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2915
3242
  * Generated bundle index. Do not edit.
2916
3243
  */
2917
3244
 
2918
- export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatMessageComponent, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, EAccountsPlatform, LangCodeDescriptionEs, ProviderSelectorComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, characterCardStringDataDefinition, extractJsonFromResponse, provideChatAIService, provideUserDataExchange };
3245
+ export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatMessageComponent, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, EAccountsPlatform, LangCodeDescriptionEs, ProviderSelectorComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, characterCardStringDataDefinition, defaultconvUserSettings, extractJsonFromResponse, provideChatAIService, provideUserDataExchange };
2919
3246
  //# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map