@dataclouder/ngx-agent-cards 0.0.87 → 0.0.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +17 -7
  2. package/fesm2022/dataclouder-ngx-agent-cards.mjs +1134 -1352
  3. package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
  4. package/lib/components/chat-container/chat-container.component.d.ts +9 -38
  5. package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +18 -10
  6. package/lib/components/chat-container/chat-header/chat-header.component.d.ts +7 -8
  7. package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +11 -27
  8. package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +3 -2
  9. package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +3 -3
  10. package/lib/components/chat-container/chat-messages-list/message-orchestrator/message-orchestrator.component.d.ts +26 -0
  11. package/lib/components/chat-settings/dc-conversation-userchat-settings.component.d.ts +7 -8
  12. package/lib/components/dc-agent-card-details/dc-agent-card-details.component.d.ts +4 -6
  13. package/lib/components/dc-agent-card-lists/agent-card-default-ui/agent-card-default-ui.component.d.ts +5 -5
  14. package/lib/components/dc-agent-card-lists/dc-agent-card-lists.component.d.ts +9 -10
  15. package/lib/components/dc-agent-form/account-platform/account-platform-form.component.d.ts +3 -4
  16. package/lib/components/dc-agent-form/dc-agent-card-form.component.d.ts +16 -18
  17. package/lib/components/icons/icons.component.d.ts +7 -7
  18. package/lib/components/prompt-preview-dialog/prompt-preview-dialog.component.d.ts +3 -2
  19. package/lib/components/provider-selector/provider-selector.component.d.ts +3 -3
  20. package/lib/components/text-highlighter/text-highlighter.d.ts +62 -0
  21. package/lib/components/translate-dialog/translate-dialog.component.d.ts +3 -5
  22. package/lib/models/agent.models.d.ts +5 -7
  23. package/lib/models/conversation-ai.class.d.ts +2 -2
  24. package/lib/models/user-data-exchange.d.ts +2 -0
  25. package/lib/pipes/safe-json.pipe.d.ts +15 -0
  26. package/lib/services/conversation.service.d.ts +8 -14
  27. package/lib/services/dc-conversation-builder.service.d.ts +0 -2
  28. package/lib/services/evaluation.service.d.ts +2 -2
  29. package/lib/services/message-processing.service.d.ts +2 -2
  30. package/package.json +1 -1
  31. package/public-api.d.ts +4 -0
  32. package/lib/components/audio-text-sync/audio-text-sync.component.d.ts +0 -42
  33. package/lib/components/chat-container/chat-messages-list/chat-message/message-content/message-content.component.d.ts +0 -24
  34. package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +0 -22
  35. package/lib/services/audio-text-sync.service.d.ts +0 -57
@@ -1,66 +1,62 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, Inject, EventEmitter, Component, Input, Output, inject, DestroyRef, signal, Injector, effect, runInInjectionContext, ChangeDetectionStrategy, Pipe, ViewChild, Optional, ViewChildren } from '@angular/core';
3
- import * as i1$2 from '@angular/common';
4
- import { CommonModule, NgComponentOutlet } from '@angular/common';
5
- import * as i1$4 from 'primeng/dynamicdialog';
6
- import { DialogService, DynamicDialogModule } from 'primeng/dynamicdialog';
7
- import * as i6 from 'primeng/dialog';
2
+ import { InjectionToken, Injectable, inject, Pipe, input, output, Input, Component, signal, computed, ChangeDetectorRef, DestroyRef, effect, ChangeDetectionStrategy, ViewChild, ViewChildren } from '@angular/core';
3
+ import * as i1$1 from '@angular/common';
4
+ import { CommonModule, DatePipe, DecimalPipe, NgComponentOutlet } from '@angular/common';
5
+ import { DynamicDialogRef, DialogService, DynamicDialogConfig, DynamicDialogModule } from 'primeng/dynamicdialog';
6
+ import * as i1$3 from 'primeng/dialog';
8
7
  import { DialogModule } from 'primeng/dialog';
9
8
  import * as i2 from 'primeng/progressbar';
10
9
  import { ProgressBarModule } from 'primeng/progressbar';
11
10
  import * as i1 from '@angular/forms';
12
- import { FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
11
+ import { FormControl, ReactiveFormsModule, FormBuilder, FormsModule } from '@angular/forms';
13
12
  import { DCMicComponent } from '@dataclouder/ngx-mic';
14
13
  import * as i3 from 'primeng/textarea';
15
14
  import { TextareaModule } from 'primeng/textarea';
16
- import * as i7 from 'primeng/button';
15
+ import * as i2$1 from 'primeng/button';
17
16
  import { ButtonModule } from 'primeng/button';
18
- import * as i1$1 from '@angular/platform-browser';
17
+ import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
19
18
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
20
- import { BehaviorSubject, Subject, fromEvent, filter } from 'rxjs';
19
+ import { Subject, fromEvent, filter } from 'rxjs';
21
20
  import { takeUntil, map } from 'rxjs/operators';
22
- import * as i1$3 from 'primeng/skeleton';
21
+ import { DomSanitizer } from '@angular/platform-browser';
22
+ import * as i1$2 from 'primeng/skeleton';
23
23
  import { SkeletonModule } from 'primeng/skeleton';
24
- import * as i4$1 from 'primeng/checkbox';
24
+ import * as i2$3 from 'primeng/checkbox';
25
25
  import { CheckboxModule } from 'primeng/checkbox';
26
26
  import { SliderModule } from 'primeng/slider';
27
- import * as i3$1 from 'primeng/radiobutton';
27
+ import * as i2$2 from 'primeng/radiobutton';
28
28
  import { RadioButtonModule } from 'primeng/radiobutton';
29
- import * as i8 from 'primeng/rating';
29
+ import * as i6$1 from 'primeng/rating';
30
30
  import { RatingModule } from 'primeng/rating';
31
- import * as i5 from 'primeng/table';
31
+ import * as i4 from 'primeng/table';
32
32
  import { TableModule } from 'primeng/table';
33
33
  import { BadgeModule } from 'primeng/badge';
34
- import * as i7$1 from 'primeng/tooltip';
34
+ import * as i6 from 'primeng/tooltip';
35
35
  import { TooltipModule } from 'primeng/tooltip';
36
- import * as i4 from 'primeng/api';
37
- import * as i6$1 from '@dataclouder/ngx-core';
38
- import { TOAST_ALERTS_TOKEN, AudioSpeed as AudioSpeed$1, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
36
+ import * as i3$1 from 'primeng/api';
37
+ import { ActivatedRoute, Router } from '@angular/router';
39
38
  import { OverlayModule } from '@angular/cdk/overlay';
40
39
  import { PortalModule } from '@angular/cdk/portal';
41
- import * as i5$1 from 'primeng/inputtext';
40
+ import * as i4$1 from 'primeng/inputtext';
42
41
  import { InputTextModule } from 'primeng/inputtext';
43
- import * as i11 from 'primeng/togglebutton';
42
+ import * as i6$2 from 'primeng/togglebutton';
44
43
  import { ToggleButtonModule } from 'primeng/togglebutton';
45
- import * as i2$2 from '@dataclouder/ngx-cloud-storage';
46
- import { AspectType, ResolutionType, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
47
- import * as i2$1 from '@angular/cdk/dialog';
48
- import { DIALOG_DATA } from '@angular/cdk/dialog';
49
- import * as i13 from 'primeng/toggleswitch';
44
+ import { ResolutionType, AspectType, MultiImagesStorageService, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
45
+ import { DialogRef, DIALOG_DATA } from '@angular/cdk/dialog';
46
+ import * as i8 from 'primeng/toggleswitch';
50
47
  import { ToggleSwitchModule } from 'primeng/toggleswitch';
51
- import * as i14 from 'primeng/select';
48
+ import * as i9 from 'primeng/select';
52
49
  import { SelectModule } from 'primeng/select';
53
- import * as i15 from 'primeng/popover';
50
+ import * as i10 from 'primeng/popover';
54
51
  import { PopoverModule } from 'primeng/popover';
55
- import * as i3$2 from 'primeng/card';
52
+ import * as i2$4 from 'primeng/card';
56
53
  import { CardModule } from 'primeng/card';
57
- import * as i4$2 from 'primeng/dropdown';
54
+ import * as i3$2 from 'primeng/dropdown';
58
55
  import { DropdownModule } from 'primeng/dropdown';
59
56
  import { ChipModule } from 'primeng/chip';
60
- import * as i1$5 from '@angular/router';
61
- import * as i3$3 from 'primeng/paginator';
57
+ import * as i1$4 from 'primeng/paginator';
62
58
  import { PaginatorModule } from 'primeng/paginator';
63
- import * as i2$3 from 'primeng/speeddial';
59
+ import * as i2$5 from 'primeng/speeddial';
64
60
  import { SpeedDialModule } from 'primeng/speeddial';
65
61
 
66
62
  const characterCardStringDataDefinition = `
@@ -87,8 +83,6 @@ class WordTimestamps {
87
83
  }
88
84
  class MessageAudio {
89
85
  }
90
- class ChatMultiMessage extends MessageAudio {
91
- }
92
86
  var ChatRole;
93
87
  (function (ChatRole) {
94
88
  ChatRole["System"] = "system";
@@ -512,10 +506,10 @@ class AudioService {
512
506
  // const srcURL = await this.filesCacheService.getURLSrcFile(path);
513
507
  this.playWithSrc(path);
514
508
  }
515
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
516
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioService, providedIn: 'root' }); }
509
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
510
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, providedIn: 'root' }); }
517
511
  }
518
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioService, decorators: [{
512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, decorators: [{
519
513
  type: Injectable,
520
514
  args: [{
521
515
  providedIn: 'root',
@@ -535,8 +529,8 @@ class UserDataExchangeAbstractService {
535
529
  }
536
530
 
537
531
  class DCConversationPromptBuilderService {
538
- constructor(userDataExchange) {
539
- this.userDataExchange = userDataExchange;
532
+ constructor() {
533
+ this.userDataExchange = inject(USER_DATA_EXCHANGE);
540
534
  }
541
535
  // For chat conversation i need inital settings.
542
536
  buildConversationSettings(agentCard, parseDict = null) {
@@ -678,27 +672,76 @@ class DCConversationPromptBuilderService {
678
672
  };
679
673
  return settings;
680
674
  }
681
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationPromptBuilderService, deps: [{ token: USER_DATA_EXCHANGE }], target: i0.ɵɵFactoryTarget.Injectable }); }
682
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationPromptBuilderService, providedIn: 'root' }); }
675
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
676
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, providedIn: 'root' }); }
683
677
  }
684
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationPromptBuilderService, decorators: [{
678
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, decorators: [{
685
679
  type: Injectable,
686
680
  args: [{
687
681
  providedIn: 'root',
688
682
  }]
689
- }], ctorParameters: () => [{ type: UserDataExchangeAbstractService, decorators: [{
690
- type: Inject,
691
- args: [USER_DATA_EXCHANGE]
692
- }] }] });
683
+ }] });
684
+
685
+ /**
686
+ * SafeJsonPipe - A pipe that safely stringifies objects with circular references
687
+ * This pipe handles circular references and DOM objects that can't be serialized
688
+ */
689
+ class SafeJsonPipe {
690
+ transform(value) {
691
+ // Create a new object with only serializable properties
692
+ return this.stringifySafely(value);
693
+ }
694
+ /**
695
+ * Safely stringify an object, handling circular references
696
+ */
697
+ stringifySafely(obj) {
698
+ const seen = new WeakSet();
699
+ return JSON.stringify(obj, (key, value) => {
700
+ // Skip properties that start with underscore (often internal properties)
701
+ if (key.startsWith('_')) {
702
+ return undefined;
703
+ }
704
+ // Handle DOM elements and other non-serializable objects
705
+ if (value instanceof HTMLElement || value instanceof Node) {
706
+ return '[DOM Element]';
707
+ }
708
+ // Handle audio elements specifically
709
+ if (key === 'audioHtml' || key === 'audioElement') {
710
+ return '[Audio Element]';
711
+ }
712
+ // Handle functions
713
+ if (typeof value === 'function') {
714
+ return '[Function]';
715
+ }
716
+ // Handle circular references
717
+ if (typeof value === 'object' && value !== null) {
718
+ if (seen.has(value)) {
719
+ return '[Circular Reference]';
720
+ }
721
+ seen.add(value);
722
+ }
723
+ return value;
724
+ }, 2);
725
+ }
726
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
727
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, isStandalone: true, name: "safeJson" }); }
728
+ }
729
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, decorators: [{
730
+ type: Pipe,
731
+ args: [{
732
+ name: 'safeJson',
733
+ standalone: true
734
+ }]
735
+ }] });
693
736
 
694
737
  class ChatHeaderComponent {
695
- constructor(agentCardService) {
696
- this.agentCardService = agentCardService;
697
- this.isAdmin = false;
738
+ constructor() {
739
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
740
+ this.isAdmin = input(false);
698
741
  this.alternativeConversation = [];
699
- this.restartConversationEvent = new EventEmitter();
700
- this.showInfoEvent = new EventEmitter();
701
- this.settingsClickEvent = new EventEmitter();
742
+ this.restartConversationEvent = output();
743
+ this.showInfoEvent = output();
744
+ this.settingsClickEvent = output();
702
745
  }
703
746
  restartConversation(conversation = null) {
704
747
  this.restartConversationEvent.emit(conversation);
@@ -723,87 +766,16 @@ class ChatHeaderComponent {
723
766
  this.alternativeConversation = conversationCards.rows;
724
767
  }
725
768
  }
726
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatHeaderComponent, deps: [{ token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
727
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatHeaderComponent, isStandalone: true, selector: "dc-chat-header", inputs: { isAdmin: "isAdmin", alternativeConversation: "alternativeConversation", agentCard: "agentCard" }, outputs: { restartConversationEvent: "restartConversationEvent", showInfoEvent: "showInfoEvent", settingsClickEvent: "settingsClickEvent" }, ngImport: i0, template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
769
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
770
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatHeaderComponent, isStandalone: true, selector: "dc-chat-header", inputs: { isAdmin: { classPropertyName: "isAdmin", publicName: "isAdmin", isSignal: true, isRequired: false, transformFunction: null }, alternativeConversation: { classPropertyName: "alternativeConversation", publicName: "alternativeConversation", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { restartConversationEvent: "restartConversationEvent", showInfoEvent: "showInfoEvent", settingsClickEvent: "settingsClickEvent" }, ngImport: i0, template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin()){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"] }); }
728
771
  }
729
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatHeaderComponent, decorators: [{
772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatHeaderComponent, decorators: [{
730
773
  type: Component,
731
- args: [{ selector: 'dc-chat-header', standalone: true, imports: [CommonModule], template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"] }]
732
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
733
- type: Inject,
734
- args: [CONVERSATION_AI_TOKEN]
735
- }] }], propDecorators: { isAdmin: [{
736
- type: Input
737
- }], alternativeConversation: [{
774
+ args: [{ selector: 'dc-chat-header', standalone: true, imports: [], template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin()){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"] }]
775
+ }], ctorParameters: () => [], propDecorators: { alternativeConversation: [{
738
776
  type: Input
739
777
  }], agentCard: [{
740
778
  type: Input
741
- }], restartConversationEvent: [{
742
- type: Output
743
- }], showInfoEvent: [{
744
- type: Output
745
- }], settingsClickEvent: [{
746
- type: Output
747
- }] } });
748
-
749
- class ChatFooterComponent {
750
- constructor() {
751
- this.isAIThinking = false;
752
- this.score = 0;
753
- this.micSettings = { useWhisper: true, lang: 'en' };
754
- this.sendMessage = new EventEmitter();
755
- this.textInputChanged = new EventEmitter();
756
- this.micFinishedEvent = new EventEmitter();
757
- this.chatInputControl = new FormControl();
758
- }
759
- /**
760
- * Sets the input text in the textarea
761
- * @param text The text to set
762
- */
763
- setInputText(text) {
764
- this.chatInputControl.setValue(text);
765
- this.textInputChanged.emit(text);
766
- }
767
- /**
768
- * Handles the mic finished event
769
- * @param eventBlob The blob event from the mic component
770
- */
771
- micFinished(eventBlob) {
772
- this.micFinishedEvent.emit(eventBlob);
773
- }
774
- /**
775
- * Sends the user message
776
- */
777
- sendUserMessage() {
778
- if (this.isAIThinking || !this.chatInputControl.value) {
779
- return;
780
- }
781
- const text = this.chatInputControl.value;
782
- const message = {
783
- content: text,
784
- role: ChatRole.User,
785
- };
786
- this.sendMessage.emit(message);
787
- this.chatInputControl.setValue('');
788
- }
789
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
790
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: "isAIThinking", score: "score", micSettings: "micSettings" }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged", micFinishedEvent: "micFinishedEvent" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
791
- }
792
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, decorators: [{
793
- type: Component,
794
- args: [{ selector: 'dc-chat-footer', standalone: true, imports: [CommonModule, ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
795
- }], propDecorators: { isAIThinking: [{
796
- type: Input
797
- }], score: [{
798
- type: Input
799
- }], micSettings: [{
800
- type: Input
801
- }], sendMessage: [{
802
- type: Output
803
- }], textInputChanged: [{
804
- type: Output
805
- }], micFinishedEvent: [{
806
- type: Output
807
779
  }] } });
808
780
 
809
781
  function markdownToHtml(markdown) {
@@ -1007,451 +979,104 @@ function removeAllEmojis(text) {
1007
979
  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, '');
1008
980
  }
1009
981
 
1010
- const ICONS = {
1011
- chat: `<svg viewBox="0 0 24 24" fill="currentColor">
1012
- <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
1013
- </svg>`,
1014
- play: `<svg
1015
- xmlns="http://www.w3.org/2000/svg"
1016
- width="16"
1017
- height="16"
1018
- viewBox="0 0 24 24"
1019
- fill="none"
1020
- stroke="#263042"
1021
- stroke-width="2"
1022
- stroke-linecap="round"
1023
- stroke-linejoin="round">
1024
- <circle cx="12" cy="12" r="10"></circle>
1025
- <polygon points="10 8 16 12 10 16 10 8"></polygon>
1026
- </svg>`,
1027
- loading: `<svg
1028
- xmlns="http://www.w3.org/2000/svg"
1029
- width="16"
1030
- height="16"
1031
- viewBox="0 0 24 24"
1032
- fill="none"
1033
- stroke="#263042"
1034
- stroke-width="2"
1035
- stroke-linecap="round"
1036
- stroke-linejoin="round">
1037
- <line x1="12" y1="2" x2="12" y2="6"></line>
1038
- <line x1="12" y1="18" x2="12" y2="22"></line>
1039
- <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
1040
- <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
1041
- <line x1="2" y1="12" x2="6" y2="12"></line>
1042
- <line x1="18" y1="12" x2="22" y2="12"></line>
1043
- <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
1044
- <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
1045
- </svg>`,
1046
- };
1047
-
1048
- class IconsComponent {
1049
- constructor(sanitizer) {
1050
- this.sanitizer = sanitizer;
1051
- this.size = 14;
1052
- this.color = 'currentColor';
1053
- }
1054
- ngOnChanges(changes) {
1055
- if (changes['name']) {
1056
- const svg = ICONS[this.name] || '';
1057
- this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
982
+ class MessageProcessingService {
983
+ constructor() { }
984
+ // Process message for display
985
+ processMessage(message, conversationSettings) {
986
+ const defaultVoice = this.getVoice(conversationSettings.voice);
987
+ let processedMessage;
988
+ processedMessage = {
989
+ content: message.content,
990
+ role: message.role,
991
+ voice: defaultVoice,
992
+ };
993
+ // Process based on text engine
994
+ if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
995
+ this.processMultiMessages(processedMessage, conversationSettings);
996
+ }
997
+ else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
998
+ if (!conversationSettings.secondaryVoice) {
999
+ throw new Error('Secondary voice is required for SSML');
1000
+ }
1001
+ const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
1002
+ processedMessage.ssml = '<speak>' + content + '</speak>';
1058
1003
  }
1004
+ console.log(processedMessage);
1005
+ return { ...message, ...processedMessage };
1059
1006
  }
1060
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
1061
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: IconsComponent, isStandalone: true, selector: "dc-icon", inputs: { name: "name", size: "size", color: "color" }, usesOnChanges: true, ngImport: i0, template: `
1062
- <span
1063
- [innerHTML]="sanitizedIcon"
1064
- [style.display]="'inline-flex'"
1065
- [style.alignItems]="'center'"
1066
- [style.justifyContent]="'center'"
1067
- [style.width.px]="size"
1068
- [style.height.px]="size"
1069
- [style.color]="color"></span>
1070
- `, isInline: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }); }
1071
- }
1072
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: IconsComponent, decorators: [{
1073
- type: Component,
1074
- args: [{ selector: 'dc-icon', template: `
1075
- <span
1076
- [innerHTML]="sanitizedIcon"
1077
- [style.display]="'inline-flex'"
1078
- [style.alignItems]="'center'"
1079
- [style.justifyContent]="'center'"
1080
- [style.width.px]="size"
1081
- [style.height.px]="size"
1082
- [style.color]="color"></span>
1083
- `, standalone: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }]
1084
- }], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { name: [{
1085
- type: Input
1086
- }], size: [{
1087
- type: Input
1088
- }], color: [{
1089
- type: Input
1090
- }] } });
1091
-
1092
- class AudioTextSyncService {
1093
- constructor() {
1094
- // Maps to store message-specific signals and observables
1095
- this.highlightedWordsSignalMap = new Map();
1096
- this.highlightedWords$Map = new Map();
1097
- // Maps for cleanup and active audio elements
1098
- this.cleanup$Map = new Map();
1099
- this.activeAudioMap = new Map();
1100
- this.destroyRef = inject(DestroyRef);
1101
- // Ensure cleanup when service is destroyed
1102
- this.destroyRef.onDestroy(() => {
1103
- this.stopAllSyncs();
1007
+ // Process multi-messages from markdown
1008
+ processMultiMessages(message, settings) {
1009
+ // Convert markdown to HTML segments
1010
+ const mdToHtml = convertToHTML(message.content);
1011
+ // Map segments to multi-messages
1012
+ message.multiMessages = mdToHtml.map((val) => {
1013
+ // Get the narrator voice from conversation settings
1014
+ const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
1015
+ // Determine if this is italicized text (narrator)
1016
+ const isItalics = val.tag === 'em';
1017
+ const voice = isItalics ? narratorVoice : message.voice;
1018
+ return {
1019
+ voice,
1020
+ content: val.content,
1021
+ audioUrl: null,
1022
+ audioPromise: null,
1023
+ text: val.text,
1024
+ tag: val.tag,
1025
+ };
1104
1026
  });
1105
1027
  }
1106
- /**
1107
- * Synchronizes audio playback with text transcription
1108
- * @param audioElement The audio element to sync with
1109
- * @param transcriptionTimestamps Array of word timestamps
1110
- * @param messageId Unique identifier for the message
1111
- */
1112
- syncAudioWithText(audioElement, transcriptionTimestamps, messageId) {
1113
- // Stop any existing sync for this message
1114
- this.stopSync(messageId);
1115
- // Create new signal and subject for this message if they don't exist
1116
- if (!this.highlightedWordsSignalMap.has(messageId)) {
1117
- this.highlightedWordsSignalMap.set(messageId, signal([]));
1118
- }
1119
- if (!this.highlightedWords$Map.has(messageId)) {
1120
- this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
1121
- }
1122
- // Create cleanup subject
1123
- const cleanup$ = new Subject();
1124
- this.cleanup$Map.set(messageId, cleanup$);
1125
- // Store the active audio element
1126
- this.activeAudioMap.set(messageId, audioElement);
1127
- // Get the signal and subject for this message
1128
- const messageSignal = this.highlightedWordsSignalMap.get(messageId);
1129
- const messageSubject = this.highlightedWords$Map.get(messageId);
1130
- // Initialize the highlighted words state
1131
- const initialWords = transcriptionTimestamps.map((word, index) => ({
1132
- word: word.word,
1133
- index,
1134
- isHighlighted: false,
1135
- }));
1136
- // Update both signal and observable
1137
- messageSignal.set(initialWords);
1138
- messageSubject.next(initialWords);
1139
- // Listen to timeupdate events
1140
- fromEvent(audioElement, 'timeupdate')
1141
- .pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$), map(() => audioElement.currentTime))
1142
- .subscribe((currentTime) => {
1143
- const updatedWords = transcriptionTimestamps.map((word, index) => {
1144
- const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
1145
- return {
1146
- word: word.word,
1147
- index,
1148
- isHighlighted,
1149
- };
1150
- });
1151
- // Update both signal and observable for this message
1152
- messageSignal.set(updatedWords);
1153
- messageSubject.next(updatedWords);
1154
- });
1155
- // Listen to ended event for cleanup
1156
- fromEvent(audioElement, 'ended')
1157
- .pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$))
1158
- .subscribe(() => {
1159
- // Reset highlighting when audio ends
1160
- const resetWords = initialWords.map((word) => ({
1161
- ...word,
1162
- isHighlighted: false,
1163
- }));
1164
- messageSignal.set(resetWords);
1165
- messageSubject.next(resetWords);
1166
- });
1028
+ // Replace italics with voice tags for SSML
1029
+ subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
1030
+ const regex = /\*(.*?)\*/g;
1031
+ return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
1167
1032
  }
1168
- /**
1169
- * Stops the sync for a specific message and cleans up resources
1170
- * @param messageId The ID of the message to stop syncing
1171
- */
1172
- stopSync(messageId) {
1173
- if (this.activeAudioMap.has(messageId)) {
1174
- // Get the cleanup subject for this message
1175
- const cleanup$ = this.cleanup$Map.get(messageId);
1176
- if (cleanup$) {
1177
- cleanup$.next();
1178
- cleanup$.complete();
1179
- this.cleanup$Map.delete(messageId);
1180
- }
1181
- this.activeAudioMap.delete(messageId);
1182
- // Reset state for this message
1183
- const messageSignal = this.highlightedWordsSignalMap.get(messageId);
1184
- const messageSubject = this.highlightedWords$Map.get(messageId);
1185
- if (messageSignal) {
1186
- messageSignal.set([]);
1187
- }
1188
- if (messageSubject) {
1189
- messageSubject.next([]);
1190
- }
1033
+ // Get appropriate voice based on settings
1034
+ getVoice(voice_value, targetLang = 'en') {
1035
+ if ([null, '', 'random'].includes(voice_value)) {
1036
+ const voiceCodes = VoiceTTSOptions.map((val) => val.id);
1037
+ return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
1191
1038
  }
1192
- }
1193
- /**
1194
- * Stops all syncs and cleans up all resources
1195
- */
1196
- stopAllSyncs() {
1197
- // Get all message IDs and stop each sync
1198
- for (const messageId of this.activeAudioMap.keys()) {
1199
- this.stopSync(messageId);
1039
+ if (voice_value === 'randomMan') {
1040
+ const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
1041
+ return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
1200
1042
  }
1201
- // Clear all maps
1202
- this.highlightedWordsSignalMap.clear();
1203
- this.highlightedWords$Map.clear();
1204
- this.cleanup$Map.clear();
1205
- this.activeAudioMap.clear();
1206
- }
1207
- /**
1208
- * Returns the highlighted words signal for a specific message
1209
- * @param messageId The ID of the message
1210
- */
1211
- getHighlightedWordsSignal(messageId) {
1212
- // Create a new signal for this message if it doesn't exist
1213
- if (!this.highlightedWordsSignalMap.has(messageId)) {
1214
- this.highlightedWordsSignalMap.set(messageId, signal([]));
1043
+ else if (voice_value === 'randomWoman') {
1044
+ const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
1045
+ return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
1215
1046
  }
1216
- return this.highlightedWordsSignalMap.get(messageId);
1217
- }
1218
- /**
1219
- * Returns the highlighted words observable for a specific message
1220
- * @param messageId The ID of the message
1221
- */
1222
- getHighlightedWords$(messageId) {
1223
- // Create a new subject for this message if it doesn't exist
1224
- if (!this.highlightedWords$Map.has(messageId)) {
1225
- this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
1047
+ else {
1048
+ const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
1049
+ if (voice) {
1050
+ return voice.id;
1051
+ }
1052
+ else {
1053
+ console.error('Voice not found getting something random', voice_value);
1054
+ return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
1055
+ }
1226
1056
  }
1227
- return this.highlightedWords$Map.get(messageId).asObservable();
1228
- }
1229
- /**
1230
- * Checks if a word at a specific index is currently highlighted for a specific message
1231
- * @param messageId The ID of the message
1232
- * @param index The index of the word to check
1233
- */
1234
- isWordHighlighted(messageId, index) {
1235
- const messageSignal = this.getHighlightedWordsSignal(messageId);
1236
- return messageSignal().some((word) => word.index === index && word.isHighlighted);
1237
- }
1238
- /**
1239
- * Returns an observable that emits true when a word at a specific index is highlighted for a specific message
1240
- * @param messageId The ID of the message
1241
- * @param index The index of the word to observe
1242
- */
1243
- isWordHighlighted$(messageId, index) {
1244
- return this.getHighlightedWords$(messageId).pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
1245
1057
  }
1246
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1247
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, providedIn: 'root' }); }
1058
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1059
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, providedIn: 'root' }); }
1248
1060
  }
1249
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncService, decorators: [{
1061
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, decorators: [{
1250
1062
  type: Injectable,
1251
1063
  args: [{
1252
1064
  providedIn: 'root',
1253
1065
  }]
1254
1066
  }], ctorParameters: () => [] });
1255
1067
 
1256
- class AudioTextSyncComponent {
1257
- constructor(cdr, audioTextSyncService) {
1258
- this.cdr = cdr;
1259
- this.audioTextSyncService = audioTextSyncService;
1260
- this.isLoading = false;
1261
- this.playAudio = new EventEmitter();
1262
- // Get the highlighted words signal from the service for this specific message
1263
- this.highlightedWords = signal([]);
1264
- // Unique ID for this message instance
1265
- this.messageId = '';
1266
- this.injector = inject(Injector);
1267
- // Setup effect to mark for check when highlighted words change
1268
- effect(() => {
1269
- // Just accessing the signal in an effect will re-run the effect when the signal changes
1270
- this.highlightedWords();
1271
- // Mark for check to ensure the component updates
1272
- this.cdr.markForCheck();
1273
- });
1274
- }
1275
- /**
1276
- * Check if the message has transcription timestamps
1277
- */
1278
- get hasTranscription() {
1279
- const hasTranscription = !!this.message.transcriptionTimestamps;
1280
- console.log('hasTranscription check:', {
1281
- hasTranscription,
1282
- transcriptionTimestamps: this.message.transcriptionTimestamps,
1283
- messageId: this.messageId,
1284
- });
1285
- return hasTranscription;
1286
- }
1287
- /**
1288
- * Get the message text content
1289
- */
1290
- get messageText() {
1291
- return this.message.content || this.message.text;
1292
- }
1293
- /**
1294
- * Track function for ngFor to improve performance
1295
- * @param index The current item's index
1296
- * @param item The current item
1297
- */
1298
- trackByIndex(index, item) {
1299
- return index;
1300
- }
1301
- ngOnInit() {
1302
- // Generate a unique ID for this message instance
1303
- this.messageId = this.generateMessageId();
1304
- // Connect to the service's signal for this message
1305
- // We need to use a different approach since we can't directly assign a read-only signal
1306
- const messageSignal = this.audioTextSyncService.getHighlightedWordsSignal(this.messageId);
1307
- // Create an effect to update our local signal when the service signal changes
1308
- // Use runInInjectionContext to properly create the effect in ngOnInit
1309
- runInInjectionContext(this.injector, () => {
1310
- effect(() => {
1311
- const words = messageSignal();
1312
- console.log('Effect updating highlightedWords:', {
1313
- words,
1314
- messageId: this.messageId,
1315
- length: words.length,
1316
- });
1317
- this.highlightedWords.set(words);
1318
- });
1319
- });
1320
- // If the message has transcription but no highlighted words are set,
1321
- // initialize them with the message's transcription
1322
- if (this.hasTranscription && this.highlightedWords().length === 0) {
1323
- const transcriptionTimestamps = this.message.transcriptionTimestamps || [];
1324
- const initialWords = transcriptionTimestamps.map((word, index) => ({
1325
- word: word.word,
1326
- index,
1327
- isHighlighted: word.highlighted || false,
1328
- }));
1329
- this.highlightedWords.set(initialWords);
1330
- // Also initialize the service signal for this message
1331
- // This ensures the words are available even before playing
1332
- this.audioTextSyncService.syncAudioWithText(this.message['audioHtml'] || new Audio(), this.message.transcriptionTimestamps || [], this.messageId);
1333
- }
1334
- }
1335
- /**
1336
- * Generate a unique ID for this message instance
1337
- * Uses message content/text and a timestamp to ensure uniqueness
1338
- */
1339
- generateMessageId() {
1340
- const messageContent = this.message.content || this.message.text || '';
1341
- const timestamp = new Date().getTime();
1342
- return `msg_${messageContent.substring(0, 20).replace(/\s+/g, '_')}_${timestamp}`;
1343
- }
1344
- /**
1345
- * Emit event to play the audio message
1346
- */
1347
- onPlayMessage() {
1348
- this.playAudio.emit(this.message);
1349
- }
1350
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: AudioTextSyncService }], target: i0.ɵɵFactoryTarget.Component }); }
1351
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AudioTextSyncComponent, isStandalone: true, selector: "dc-audio-text-sync", inputs: { message: "message", isLoading: "isLoading" }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n\n <!-- Display transcription with highlighting -->\n <div>\n @if (hasTranscription) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n\n } }@else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText\"></span>\n }\n </div>\n</div>\n", styles: [".highlight{background-color:#ff0;border-radius:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1352
- }
1353
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AudioTextSyncComponent, decorators: [{
1354
- type: Component,
1355
- args: [{ selector: 'dc-audio-text-sync', standalone: true, imports: [CommonModule, IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n\n <!-- Display transcription with highlighting -->\n <div>\n @if (hasTranscription) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n\n } }@else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText\"></span>\n }\n </div>\n</div>\n", styles: [".highlight{background-color:#ff0;border-radius:3px}\n"] }]
1356
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: AudioTextSyncService }], propDecorators: { message: [{
1357
- type: Input
1358
- }], isLoading: [{
1359
- type: Input
1360
- }], playAudio: [{
1361
- type: Output
1362
- }] } });
1363
-
1364
- class MessageContentComponent {
1365
- constructor() {
1366
- this.isLoading = false;
1367
- this.playAudio = new EventEmitter();
1368
- }
1369
- get hasTranscription() {
1370
- return !!this.message.transcriptionTimestamps;
1371
- }
1372
- get messageText() {
1373
- return this.message.content || this.message.text;
1374
- }
1375
- get messageTag() {
1376
- return this.message.tag || null;
1377
- }
1378
- onPlayMessage() {
1379
- this.playAudio.emit(this.message);
1380
- }
1381
- /**
1382
- * Track function for ngFor to improve performance
1383
- * @param index The current item's index
1384
- * @param item The current item
1385
- */
1386
- trackByIndex(index, item) {
1387
- return index;
1388
- }
1389
- ngOnInit() {
1390
- console.log(this.message);
1391
- }
1392
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1393
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: MessageContentComponent, isStandalone: true, selector: "dc-message-content", inputs: { message: "message", isLoading: "isLoading" }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "<!-- Message with transcription -->\n@if (hasTranscription) {\n<dc-audio-text-sync [message]=\"message\" [isLoading]=\"isLoading\" (playAudio)=\"playAudio.emit($event)\"></dc-audio-text-sync>\n}@else {\n<!-- Message without transcription -->\n<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n <span style=\"margin-left: 2px\" [ngClass]=\"messageTag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n}\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: AudioTextSyncComponent, selector: "dc-audio-text-sync", inputs: ["message", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1394
- }
1395
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageContentComponent, decorators: [{
1396
- type: Component,
1397
- args: [{ selector: 'dc-message-content', standalone: true, imports: [CommonModule, IconsComponent, AudioTextSyncComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Message with transcription -->\n@if (hasTranscription) {\n<dc-audio-text-sync [message]=\"message\" [isLoading]=\"isLoading\" (playAudio)=\"playAudio.emit($event)\"></dc-audio-text-sync>\n}@else {\n<!-- Message without transcription -->\n<div style=\"display: flex\">\n <!-- Icon for play/loading status -->\n <i (click)=\"onPlayMessage()\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n <span style=\"margin-left: 2px\" [ngClass]=\"messageTag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n}\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
1398
- }], ctorParameters: () => [], propDecorators: { message: [{
1399
- type: Input
1400
- }], isLoading: [{
1401
- type: Input
1402
- }], playAudio: [{
1403
- type: Output
1404
- }] } });
1405
-
1406
- class MultiMessageContentComponent {
1407
- constructor() {
1408
- this.isLoading = false;
1409
- this.playAudio = new EventEmitter();
1410
- }
1411
- /**
1412
- * Checks if a message has transcription timestamps
1413
- * @param message The message to check
1414
- */
1415
- hasTranscription(message) {
1416
- // debugger;
1417
- return !!message.transcriptionTimestamps;
1418
- }
1419
- /**
1420
- * Emits the playAudio event with the selected message
1421
- * @param message The message to play
1422
- */
1423
- onPlayMessage(message) {
1424
- this.playAudio.emit(message);
1425
- }
1426
- ngOnInit() {
1427
- console.log(this.messages);
1428
- }
1429
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiMessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1430
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: MultiMessageContentComponent, isStandalone: true, selector: "dc-multi-message-content", inputs: { messages: "messages", isLoading: "isLoading" }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "@for (message of messages; track message) {\n<!-- Message with transcription -->\n@if (hasTranscription(message)) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <dc-audio-text-sync [message]=\"message\"></dc-audio-text-sync>\n</div>\n}@else {\n<!-- Message without transcription -->\n\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n} }\n\n<!-- Icon template for play/loading status -->\n<ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n</ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: AudioTextSyncComponent, selector: "dc-audio-text-sync", inputs: ["message", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1431
- }
1432
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MultiMessageContentComponent, decorators: [{
1433
- type: Component,
1434
- args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [CommonModule, IconsComponent, AudioTextSyncComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messages; track message) {\n<!-- Message with transcription -->\n@if (hasTranscription(message)) {\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <dc-audio-text-sync [message]=\"message\"></dc-audio-text-sync>\n</div>\n}@else {\n<!-- Message without transcription -->\n\n<div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading, message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n</div>\n} }\n\n<!-- Icon template for play/loading status -->\n<ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n <dc-icon *ngIf=\"isLoading\" class=\"spin-animation\" name=\"loading\"></dc-icon>\n <dc-icon *ngIf=\"!isLoading\" name=\"play\"></dc-icon>\n </i>\n</ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
1435
- }], ctorParameters: () => [], propDecorators: { messages: [{
1436
- type: Input
1437
- }], isLoading: [{
1438
- type: Input
1439
- }], playAudio: [{
1440
- type: Output
1441
- }] } });
1442
-
1443
- function matchTranscription(originalText, transcription) {
1444
- const result = [];
1445
- const transcriptionMap = new Map();
1446
- // Create a map of lowercase words to an array of their corresponding objects in transcription
1447
- for (const obj of transcription) {
1448
- const lowercaseWord = obj.word.trim().toLowerCase();
1449
- if (transcriptionMap.has(lowercaseWord)) {
1450
- transcriptionMap.get(lowercaseWord).push(obj);
1451
- }
1452
- else {
1453
- transcriptionMap.set(lowercaseWord, [obj]);
1454
- }
1068
+ function matchTranscription(originalText, transcription) {
1069
+ const result = [];
1070
+ const transcriptionMap = new Map();
1071
+ // Create a map of lowercase words to an array of their corresponding objects in transcription
1072
+ for (const obj of transcription) {
1073
+ const lowercaseWord = obj.word.trim().toLowerCase();
1074
+ if (transcriptionMap.has(lowercaseWord)) {
1075
+ transcriptionMap.get(lowercaseWord).push(obj);
1076
+ }
1077
+ else {
1078
+ transcriptionMap.set(lowercaseWord, [obj]);
1079
+ }
1455
1080
  }
1456
1081
  // Split the original text into an array of words
1457
1082
  const words = originalText.split(' ');
@@ -1484,334 +1109,78 @@ function extractAudioAndTranscription(message, audio) {
1484
1109
  if (message.transcription) {
1485
1110
  message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
1486
1111
  }
1112
+ return message;
1487
1113
  }
1488
-
1489
- class ChatMessageComponent {
1490
- constructor(audioService, agentCardService, audioTextSyncService) {
1491
- this.audioService = audioService;
1492
- this.agentCardService = agentCardService;
1493
- this.audioTextSyncService = audioTextSyncService;
1494
- this.destroyRef = inject(DestroyRef);
1495
- this.conversationChatSettings = signal(null);
1496
- this.isLoading = signal(false);
1497
- }
1498
- async ngOnInit() {
1499
- const settings = await this.agentCardService.getConversationUserChatSettings();
1500
- this.conversationChatSettings.set(settings);
1501
- if (this.chatMessage.role === ChatRole.AssistantHelper) {
1502
- return;
1503
- }
1504
- const settings$ = this.conversationChatSettings();
1505
- if (this.chatMessage.role === ChatRole.User) {
1506
- if (settings$?.repeatRecording && settings$?.synthVoice) {
1507
- this.playMessage(this.chatMessage);
1508
- }
1509
- }
1510
- else if (settings$?.synthVoice) {
1511
- if (this.chatMessage.multiMessages) {
1512
- this.generateAndPlayAllAudios(this.chatMessage.multiMessages);
1513
- }
1514
- else {
1515
- this.generateAndPlayAudio(this.chatMessage);
1516
- }
1517
- }
1518
- }
1519
- async generateAndPlayAudio(message, overwriteText = null) {
1520
- this.isLoading.set(true);
1521
- try {
1522
- const text = overwriteText || message.content;
1523
- const ttsObject = this.buildObjectTTSRequest({ ...this.chatMessage, text });
1524
- if (message.ssml) {
1525
- ttsObject.ssml = message.ssml;
1526
- }
1527
- const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
1528
- extractAudioAndTranscription(message, speechAudio);
1529
- this.playMessage(message);
1530
- }
1531
- finally {
1532
- this.isLoading.set(false);
1533
- }
1534
- }
1535
- playMessage(message) {
1536
- if (!message.audioUrl) {
1537
- return null;
1538
- }
1539
- const audioElement = this.audioService.playAudio(message.audioUrl);
1540
- message['audioHtml'] = audioElement;
1541
- console.log('Playing message with transcription:', {
1542
- hasTranscription: !!message.transcriptionTimestamps,
1543
- transcriptionLength: message.transcriptionTimestamps?.length,
1544
- audioUrl: message.audioUrl,
1545
- audioElement,
1546
- });
1547
- if (message.transcriptionTimestamps) {
1548
- // Generate a unique ID for this message
1549
- const messageId = this.generateMessageId(message);
1550
- console.log('Syncing audio with text:', {
1551
- messageId,
1552
- transcriptionTimestamps: message.transcriptionTimestamps,
1553
- wordCount: message.transcriptionTimestamps.length,
1554
- });
1555
- // Use the audio-text sync service instead of direct manipulation
1556
- // Pass the messageId to ensure each message has its own state
1557
- this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
1558
- }
1559
- return audioElement;
1560
- }
1561
- /**
1562
- * Generate a unique ID for a message
1563
- * Uses message content/text and a timestamp to ensure uniqueness
1564
- */
1565
- generateMessageId(message) {
1566
- const messageContent = message.content || message.text || '';
1567
- // Use a safe approach to get an ID-like value since 'id' isn't guaranteed on these types
1568
- const messageId = message._id || message.id || '';
1569
- const timestamp = new Date().getTime();
1570
- return `msg_${messageId}_${messageContent.substring(0, 10).replace(/\s+/g, '_')}_${timestamp}`;
1571
- }
1572
- // This method is no longer needed as we're using the AudioTextSyncService
1573
- // It's kept here as a reference but can be removed
1574
- /*
1575
- private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
1576
- // This functionality has been moved to AudioTextSyncService
1577
- }
1578
- */
1579
- generateAndPlayAllAudios(multiMessages) {
1580
- if (!multiMessages.length)
1581
- return;
1582
- let currentIndex = 0;
1583
- const playAudioSequentially = async () => {
1584
- if (currentIndex >= multiMessages.length) {
1585
- return;
1586
- }
1587
- const currentMessage = multiMessages[currentIndex];
1588
- currentMessage.isLoading = true;
1589
- try {
1590
- // Process current message audio if needed
1591
- if (!currentMessage.audioUrl) {
1592
- if (currentMessage.audioPromise) {
1593
- await currentMessage.audioPromise;
1594
- }
1595
- else {
1596
- const request = this.buildObjectTTSRequest(currentMessage);
1597
- currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
1598
- const audio = await currentMessage.audioPromise;
1599
- extractAudioAndTranscription(currentMessage, audio);
1600
- }
1601
- }
1602
- // Play the current message
1603
- const audioElement = this.playMessage(currentMessage);
1604
- if (audioElement) {
1605
- audioElement.addEventListener('ended', () => {
1606
- currentIndex++;
1607
- playAudioSequentially();
1608
- });
1609
- }
1610
- else {
1611
- // If playback failed, move to next message
1612
- currentIndex++;
1613
- playAudioSequentially();
1614
- }
1615
- // Preload next message audio
1616
- this.preloadNextMessageAudio(multiMessages, currentIndex);
1617
- }
1618
- finally {
1619
- currentMessage.isLoading = false;
1620
- }
1621
- };
1622
- // Start the sequential playback
1623
- playAudioSequentially();
1624
- }
1625
- preloadNextMessageAudio(messages, currentIndex) {
1626
- if (currentIndex + 1 < messages.length) {
1627
- const nextMessage = messages[currentIndex + 1];
1628
- if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
1629
- const request = this.buildObjectTTSRequest(nextMessage);
1630
- nextMessage.isLoading = true;
1631
- nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
1632
- nextMessage.audioPromise.then((audio) => {
1633
- extractAudioAndTranscription(nextMessage, audio);
1634
- nextMessage.isLoading = false;
1635
- });
1636
- }
1637
- }
1638
- }
1639
- buildObjectTTSRequest(message) {
1640
- const settings = this.conversationChatSettings();
1641
- const generateTranscription = settings?.highlightWords ?? false;
1642
- const speedRate = settings?.speedRate || 0;
1643
- const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed.Regular;
1644
- const text = removeEmojis(message.text || message.content);
1645
- return {
1646
- text,
1647
- voice: message.voice || this.chatMessage.voice,
1648
- generateTranscription,
1649
- speedRate,
1650
- speed,
1651
- };
1652
- }
1653
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, deps: [{ token: AudioService }, { token: CONVERSATION_AI_TOKEN }, { token: AudioTextSyncService }], target: i0.ɵɵFactoryTarget.Component }); }
1654
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: "chatMessage", chatUserSettings: "chatUserSettings" }, ngImport: i0, template: "<!-- Multi-message display -->\n<span class=\"message-container\">\n @if (chatMessage?.multiMessages) {\n <!-- Single message display -->\n <dc-multi-message-content [messages]=\"chatMessage.multiMessages\" (playAudio)=\"playMessage($event)\"> </dc-multi-message-content>\n } @else {\n <dc-message-content [message]=\"chatMessage\" [isLoading]=\"isLoading()\" (playAudio)=\"playMessage($event)\"> </dc-message-content>\n }\n</span>\n\n@if (chatMessage.translation) {\n<!-- Translation display if available -->\n\n<div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n}\n", styles: [":host{display:block}.message-container{display:block;line-height:1.5}::ng-deep .em{color:#0d5878;font-style:italic}::ng-deep .strong{font-weight:700;color:#515151}::ng-deep .em_strong{font-weight:700;font-style:italic;color:#0d5878}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: MessageContentComponent, selector: "dc-message-content", inputs: ["message", "isLoading"], outputs: ["playAudio"] }, { kind: "component", type: MultiMessageContentComponent, selector: "dc-multi-message-content", inputs: ["messages", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1655
- }
1656
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, decorators: [{
1657
- type: Component,
1658
- args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MessageContentComponent, MultiMessageContentComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Multi-message display -->\n<span class=\"message-container\">\n @if (chatMessage?.multiMessages) {\n <!-- Single message display -->\n <dc-multi-message-content [messages]=\"chatMessage.multiMessages\" (playAudio)=\"playMessage($event)\"> </dc-multi-message-content>\n } @else {\n <dc-message-content [message]=\"chatMessage\" [isLoading]=\"isLoading()\" (playAudio)=\"playMessage($event)\"> </dc-message-content>\n }\n</span>\n\n@if (chatMessage.translation) {\n<!-- Translation display if available -->\n\n<div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n}\n", styles: [":host{display:block}.message-container{display:block;line-height:1.5}::ng-deep .em{color:#0d5878;font-style:italic}::ng-deep .strong{font-weight:700;color:#515151}::ng-deep .em_strong{font-weight:700;font-style:italic;color:#0d5878}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"] }]
1659
- }], ctorParameters: () => [{ type: AudioService }, { type: AgentCardsAbstractService, decorators: [{
1660
- type: Inject,
1661
- args: [CONVERSATION_AI_TOKEN]
1662
- }] }, { type: AudioTextSyncService }], propDecorators: { chatMessage: [{
1663
- type: Input
1664
- }], chatUserSettings: [{
1665
- type: Input
1666
- }] } });
1667
-
1668
- class MessageProcessingService {
1669
- constructor() { }
1670
- // Process message for display
1671
- processMessage(message, conversationSettings, mutate = false) {
1672
- const defaultVoice = this.getVoice(conversationSettings.voice);
1673
- let processedMessage;
1674
- if (mutate) {
1675
- // Modify the existing message object
1676
- message.voice = defaultVoice;
1677
- processedMessage = message;
1678
- }
1679
- else {
1680
- // Create a new message object
1681
- processedMessage = {
1682
- content: message.content,
1683
- role: message.role,
1684
- voice: defaultVoice
1685
- };
1686
- }
1687
- // Process based on text engine
1688
- if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
1689
- this.processMultiMessages(processedMessage, conversationSettings);
1690
- }
1691
- else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
1692
- if (!conversationSettings.secondaryVoice) {
1693
- throw new Error('Secondary voice is required for SSML');
1694
- }
1695
- const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
1696
- processedMessage.ssml = '<speak>' + content + '</speak>';
1697
- }
1698
- return processedMessage;
1699
- }
1700
- // Process multi-messages from markdown
1701
- processMultiMessages(message, settings) {
1702
- // Convert markdown to HTML segments
1703
- const mdToHtml = convertToHTML(message.content);
1704
- // Map segments to multi-messages
1705
- message.multiMessages = mdToHtml.map((val) => {
1706
- // Get the narrator voice from conversation settings
1707
- const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
1708
- // Determine if this is italicized text (narrator)
1709
- const isItalics = val.tag === 'em';
1710
- const voice = isItalics ? narratorVoice : message.voice;
1711
- return {
1712
- voice,
1713
- content: val.content,
1714
- audioUrl: null,
1715
- audioPromise: null,
1716
- text: val.text,
1717
- tag: val.tag
1718
- };
1719
- });
1720
- }
1721
- // Replace italics with voice tags for SSML
1722
- subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
1723
- const regex = /\*(.*?)\*/g;
1724
- return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
1725
- }
1726
- // Get appropriate voice based on settings
1727
- getVoice(voice_value, targetLang = 'en') {
1728
- if ([null, '', 'random'].includes(voice_value)) {
1729
- const voiceCodes = VoiceTTSOptions.map((val) => val.id);
1730
- return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
1731
- }
1732
- if (voice_value === 'randomMan') {
1733
- const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
1734
- return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
1735
- }
1736
- else if (voice_value === 'randomWoman') {
1737
- const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
1738
- return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
1739
- }
1740
- else {
1741
- const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
1742
- if (voice) {
1743
- return voice.id;
1744
- }
1745
- else {
1746
- console.error('Voice not found getting something random', voice_value);
1747
- return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
1748
- }
1749
- }
1750
- }
1751
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1752
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, providedIn: 'root' }); }
1114
+ function buildObjectTTSRequest(message, settings = {}) {
1115
+ const generateTranscription = settings?.highlightWords ?? false;
1116
+ const speedRate = settings?.speedRate || 0;
1117
+ const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
1118
+ const text = removeEmojis(message.text || message.content);
1119
+ return {
1120
+ text,
1121
+ voice: message.voice || settings.voice,
1122
+ generateTranscription,
1123
+ speedRate,
1124
+ speed,
1125
+ };
1753
1126
  }
1754
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, decorators: [{
1755
- type: Injectable,
1756
- args: [{
1757
- providedIn: 'root',
1758
- }]
1759
- }], ctorParameters: () => [] });
1760
1127
 
1761
1128
  class ConversationService {
1762
- constructor(agentCardService, messageProcessingService) {
1763
- this.agentCardService = agentCardService;
1764
- this.messageProcessingService = messageProcessingService;
1129
+ constructor() {
1130
+ // Services
1131
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
1132
+ this.messageProcessingService = inject(MessageProcessingService);
1133
+ this.conversationBuilder = inject(DCConversationPromptBuilderService);
1134
+ this.userDataExchange = inject(USER_DATA_EXCHANGE);
1135
+ // Signals
1765
1136
  this.messagesSignal = signal([]);
1766
1137
  this.isThinkingSignal = signal(false);
1767
1138
  this.conversationSettingsSignal = signal(null);
1768
- this.agentCardSignal = signal(null);
1769
1139
  this.isDestroyedSignal = signal(false);
1770
1140
  }
1771
1141
  // Get messages as a signal
1772
- getMessages() {
1142
+ getMessagesSignal() {
1773
1143
  return this.messagesSignal;
1774
1144
  }
1145
+ // Add message to conversation
1146
+ addMessage(message) {
1147
+ this.messagesSignal.update((messages) => [...messages, message]);
1148
+ }
1775
1149
  // Get thinking state as a signal
1776
1150
  isThinking() {
1777
1151
  return this.isThinkingSignal;
1778
1152
  }
1779
- // Get conversation settings as a signal
1780
- getConversationSettings() {
1781
- return this.conversationSettingsSignal;
1782
- }
1783
- // Get agent card as a signal
1784
- getAgentCard() {
1785
- return this.agentCardSignal;
1786
- }
1787
1153
  // Set destroyed state
1788
1154
  setDestroyed(value) {
1789
1155
  this.isDestroyedSignal.set(value);
1790
1156
  }
1791
- // Initialize conversation
1792
- async initConversation(agentCard, conversationBuilder, parseDict) {
1793
- if (!agentCard?.conversationSettings) {
1794
- throw new Error('Conversation settings are required');
1157
+ setupConversationWithAgentCard(agentCard) {
1158
+ const conversationSettings = this.conversationBuilder.buildConversationSettings(agentCard);
1159
+ // set initial ids
1160
+ for (const i in conversationSettings.messages) {
1161
+ conversationSettings.messages[i].messageId = 'msg_' + i;
1795
1162
  }
1796
- this.agentCardSignal.set(agentCard);
1797
- // Build conversation settings
1798
- const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
1799
1163
  this.conversationSettingsSignal.set(conversationSettings);
1800
- // Update agent card with conversation settings
1801
- const updatedAgentCard = { ...agentCard, conversationSettings };
1802
- this.agentCardSignal.set(updatedAgentCard);
1803
- if (!conversationSettings.messages) {
1804
- throw new Error('conversationSettings.messages is required in proper format to start conversation');
1805
- }
1806
- // Set initial messages
1807
- this.messagesSignal.set(conversationSettings.messages);
1164
+ }
1165
+ // Initialize conversation
1166
+ async initConversation(agentCard) {
1167
+ this.setupConversationWithAgentCard(agentCard);
1168
+ const conversationSettings = this.conversationSettingsSignal();
1808
1169
  // Find first assistant message
1809
1170
  const firstAssistantMsg = conversationSettings.messages.find((message) => message.role === ChatRole.Assistant);
1810
1171
  if (firstAssistantMsg) {
1811
1172
  // Process the first assistant message
1812
- this.processAssistantMessage(firstAssistantMsg, true);
1173
+ const processedMessage = this.messageProcessingService.processMessage(firstAssistantMsg, this.conversationSettingsSignal());
1174
+ // Find the index of the message with the matching ID
1175
+ const messageIndex = conversationSettings.messages.findIndex((message) => message.messageId === firstAssistantMsg.messageId);
1176
+ // If found, replace the message at that index
1177
+ if (messageIndex !== -1) {
1178
+ conversationSettings.messages[messageIndex] = processedMessage;
1179
+ }
1180
+ this.messagesSignal.set(conversationSettings.messages);
1181
+ // this.addMessage(processedMessage);
1813
1182
  }
1814
- else if (agentCard.conversationSettings.autoStart) {
1183
+ else if (conversationSettings.autoStart) {
1815
1184
  // Auto-start conversation if configured
1816
1185
  await this.sendCurrentConversation();
1817
1186
  }
@@ -1826,135 +1195,771 @@ class ConversationService {
1826
1195
  // Set thinking state
1827
1196
  this.isThinkingSignal.set(true);
1828
1197
  try {
1829
- if (message.audioUrl && this.getAudioPlaybackSetting()) {
1830
- // Wait for audio to finish playing before sending response
1831
- await new Promise((resolve) => {
1832
- if (message.audioHtml) {
1833
- message.audioHtml.addEventListener('ended', () => {
1834
- resolve();
1835
- });
1836
- }
1837
- else {
1838
- resolve();
1839
- }
1840
- });
1841
- }
1842
1198
  // Send to AI service
1843
1199
  await this.sendCurrentConversation();
1844
1200
  }
1845
- finally {
1846
- this.isThinkingSignal.set(false);
1201
+ finally {
1202
+ this.isThinkingSignal.set(false);
1203
+ }
1204
+ }
1205
+ // Process assistant message
1206
+ // Send current conversation to AI
1207
+ async sendCurrentConversation() {
1208
+ if (this.isDestroyedSignal()) {
1209
+ return;
1210
+ }
1211
+ const messages = this.messagesSignal();
1212
+ const conversationSettings = this.conversationSettingsSignal();
1213
+ if (messages.length > 31) {
1214
+ // Safety limit to prevent infinite conversations
1215
+ return;
1216
+ }
1217
+ let conversationMessages = messages;
1218
+ // Add last prompt if available
1219
+ if (conversationSettings.last_prompt) {
1220
+ conversationMessages = [...messages, { content: conversationSettings.last_prompt, role: ChatRole.System }];
1221
+ }
1222
+ // Prepare conversation DTO
1223
+ const conversation = {
1224
+ messages: conversationMessages,
1225
+ conversationType: conversationSettings.conversationType,
1226
+ textEngine: conversationSettings.textEngine,
1227
+ model: { modelName: '', provider: '' },
1228
+ };
1229
+ console.warn('fix model name modelName take from user settings');
1230
+ // Call AI service
1231
+ const response = await this.agentCardService.callChatCompletion(conversation);
1232
+ if (!response) {
1233
+ console.error('No message returned from AI, is your service working?');
1234
+ throw new Error('No message returned from AI');
1235
+ }
1236
+ // Process response
1237
+ const newMessage = this.messageProcessingService.processMessage(response, conversationSettings);
1238
+ // Add to messages
1239
+ this.addMessage(newMessage);
1240
+ this.isThinkingSignal.set(false);
1241
+ }
1242
+ // Reset conversation
1243
+ async resetConversation(agentCard) {
1244
+ // Clear messages
1245
+ this.messagesSignal.set([]);
1246
+ }
1247
+ async getTTSFile(message) {
1248
+ const userChatSettings = await this.userDataExchange.getUserChatSettings();
1249
+ const ttsRequest = buildObjectTTSRequest(message, userChatSettings);
1250
+ return this.agentCardService.getTextAudioFile(ttsRequest);
1251
+ }
1252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1253
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
1254
+ }
1255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, decorators: [{
1256
+ type: Injectable,
1257
+ args: [{
1258
+ providedIn: 'root',
1259
+ }]
1260
+ }] });
1261
+
1262
+ function extractJsonFromResponse(content) {
1263
+ const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
1264
+ if (!jsonMatch)
1265
+ return null;
1266
+ try {
1267
+ return JSON.parse(jsonMatch[0]);
1268
+ }
1269
+ catch (error) {
1270
+ console.error('Error parsing JSON:', error);
1271
+ return null;
1272
+ }
1273
+ }
1274
+
1275
+ class EvaluationService {
1276
+ constructor() {
1277
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
1278
+ this.scoreSignal = signal(10);
1279
+ this.evaluationResultSignal = signal(null);
1280
+ }
1281
+ // Get score as a signal
1282
+ getScore() {
1283
+ return this.scoreSignal;
1284
+ }
1285
+ // Get evaluation result as a signal
1286
+ getEvaluationResult() {
1287
+ return this.evaluationResultSignal;
1288
+ }
1289
+ // Evaluate conversation
1290
+ async evaluateConversation(messages, evaluator) {
1291
+ // Filter conversation to only include user and assistant messages
1292
+ const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
1293
+ // Check if there are any user messages to evaluate
1294
+ const userMessages = conversationMessages.filter((message) => message.role === ChatRole.User);
1295
+ if (userMessages.length <= 0) {
1296
+ console.log('No messages to evaluate', conversationMessages);
1297
+ return;
1298
+ }
1299
+ // Format conversation for evaluation
1300
+ const conversationMessagesString = conversationMessages
1301
+ .map((message) => `${message.role}: ${message.content}`)
1302
+ .join('\n');
1303
+ // Create evaluation prompt
1304
+ const instructions = `
1305
+ Please replay to this task:
1306
+ ${evaluator.task}
1307
+ This is the conversation history:
1308
+ ${conversationMessagesString}
1309
+ and give the response in next JSON format.
1310
+ ${evaluator.expectedResponseType}
1311
+ `;
1312
+ // Send evaluation request
1313
+ const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
1314
+ const response = await this.agentCardService.callChatCompletion({ messages: evaluationMessages });
1315
+ // Extract JSON from response
1316
+ const jsonData = extractJsonFromResponse(response.content);
1317
+ this.evaluationResultSignal.set(jsonData);
1318
+ // Update score if available
1319
+ if (jsonData.score) {
1320
+ if (jsonData.score <= 3) {
1321
+ this.updateScore(jsonData.score * 10);
1322
+ }
1323
+ }
1324
+ }
1325
+ // Update score with limits
1326
+ updateScore(additionalScore) {
1327
+ this.scoreSignal.update(currentScore => {
1328
+ const newScore = currentScore + additionalScore;
1329
+ return newScore > 100 ? 100 : newScore;
1330
+ });
1331
+ }
1332
+ // Reset score
1333
+ resetScore() {
1334
+ this.scoreSignal.set(10);
1335
+ }
1336
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1337
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, providedIn: 'root' }); }
1338
+ }
1339
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, decorators: [{
1340
+ type: Injectable,
1341
+ args: [{
1342
+ providedIn: 'root',
1343
+ }]
1344
+ }], ctorParameters: () => [] });
1345
+
1346
+ class ChatFooterComponent {
1347
+ constructor() {
1348
+ this.conversationService = inject(ConversationService);
1349
+ this.evaluationService = inject(EvaluationService);
1350
+ this.isAIThinking = input(false);
1351
+ this.evaluatorAgentCard = input();
1352
+ this.micSettings = input({ useWhisper: true, lang: 'en' });
1353
+ this.sendMessage = output();
1354
+ this.textInputChanged = output();
1355
+ // readonly micFinishedEvent = output<any>();
1356
+ this.isGettingTranscription = false;
1357
+ this.isUserTalking = false;
1358
+ this.chatInputControl = new FormControl();
1359
+ // Get score from evaluation service
1360
+ this.score = this.evaluationService.getScore();
1361
+ }
1362
+ /**
1363
+ * Sets the input text in the textarea
1364
+ * @param text The text to set
1365
+ */
1366
+ setInputText(text) {
1367
+ this.chatInputControl.setValue(text);
1368
+ this.textInputChanged.emit(text);
1369
+ }
1370
+ /**
1371
+ * Handles the mic finished event
1372
+ * @param eventBlob The blob event from the mic component
1373
+ */
1374
+ micFinished(eventBlob) {
1375
+ // this.micFinishedEvent.emit(eventBlob);
1376
+ }
1377
+ /**
1378
+ * Sends the user message
1379
+ */
1380
+ async sendUserMessage() {
1381
+ if (this.isAIThinking() || !this.chatInputControl.value) {
1382
+ return;
1383
+ }
1384
+ const text = this.chatInputControl.value;
1385
+ const message = {
1386
+ content: text,
1387
+ role: ChatRole.User,
1388
+ };
1389
+ // Emit the message for parent components that need it
1390
+ this.sendMessage.emit(message);
1391
+ // Clear the input field
1392
+ this.chatInputControl.setValue('');
1393
+ // Send the user message to the conversation service
1394
+ await this.conversationService.sendUserMessage(message);
1395
+ // Evaluate conversation after sending message
1396
+ this.evaluateConversation();
1397
+ }
1398
+ /**
1399
+ * Evaluate conversation using evaluator agent
1400
+ */
1401
+ async evaluateConversation() {
1402
+ const messages = this.conversationService.getMessagesSignal()();
1403
+ if (this.evaluatorAgentCard()) {
1404
+ await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard());
1405
+ }
1406
+ }
1407
+ async onMicFinished(eventBlob) {
1408
+ if (!(eventBlob instanceof Blob)) {
1409
+ return;
1410
+ }
1411
+ // Check if audio is too small
1412
+ if (eventBlob.size < 10000) {
1413
+ return;
1414
+ }
1415
+ this.isUserTalking = false;
1416
+ this.isGettingTranscription = true;
1417
+ try {
1418
+ // Get transcription from audio
1419
+ // const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, {
1420
+ // conversationId: this.agentCard._id,
1421
+ // });
1422
+ // Create message with transcription
1423
+ // const message: ChatMessage = {
1424
+ // content: transcription.text,
1425
+ // role: ChatRole.User,
1426
+ // transcriptionTimestamps: transcription.words,
1427
+ // };
1428
+ const message = {
1429
+ content: '',
1430
+ role: ChatRole.User,
1431
+ audioUrl: URL.createObjectURL(eventBlob),
1432
+ };
1433
+ // Send message to conversation
1434
+ // The evaluation will happen automatically in the conversation service
1435
+ await this.conversationService.sendUserMessage(message);
1436
+ }
1437
+ finally {
1438
+ this.isGettingTranscription = false;
1439
+ }
1440
+ }
1441
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1442
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: { classPropertyName: "isAIThinking", publicName: "isAIThinking", isSignal: true, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, micSettings: { classPropertyName: "micSettings", publicName: "micSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
1443
+ }
1444
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
1445
+ type: Component,
1446
+ args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
1447
+ }] });
1448
+
1449
+ const ICONS = {
1450
+ chat: `<svg viewBox="0 0 24 24" fill="currentColor">
1451
+ <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
1452
+ </svg>`,
1453
+ play: `<svg
1454
+ xmlns="http://www.w3.org/2000/svg"
1455
+ width="16"
1456
+ height="16"
1457
+ viewBox="0 0 24 24"
1458
+ fill="none"
1459
+ stroke="#263042"
1460
+ stroke-width="2"
1461
+ stroke-linecap="round"
1462
+ stroke-linejoin="round">
1463
+ <circle cx="12" cy="12" r="10"></circle>
1464
+ <polygon points="10 8 16 12 10 16 10 8"></polygon>
1465
+ </svg>`,
1466
+ loading: `<svg
1467
+ xmlns="http://www.w3.org/2000/svg"
1468
+ width="16"
1469
+ height="16"
1470
+ viewBox="0 0 24 24"
1471
+ fill="none"
1472
+ stroke="#263042"
1473
+ stroke-width="2"
1474
+ stroke-linecap="round"
1475
+ stroke-linejoin="round">
1476
+ <line x1="12" y1="2" x2="12" y2="6"></line>
1477
+ <line x1="12" y1="18" x2="12" y2="22"></line>
1478
+ <line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
1479
+ <line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
1480
+ <line x1="2" y1="12" x2="6" y2="12"></line>
1481
+ <line x1="18" y1="12" x2="22" y2="12"></line>
1482
+ <line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
1483
+ <line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
1484
+ </svg>`,
1485
+ };
1486
+
1487
+ class IconsComponent {
1488
+ constructor() {
1489
+ this.sanitizer = inject(DomSanitizer);
1490
+ this.name = input.required();
1491
+ this.size = input(14);
1492
+ this.color = input('currentColor');
1493
+ }
1494
+ ngOnChanges(changes) {
1495
+ if (changes['name']) {
1496
+ const svg = ICONS[this.name()] || '';
1497
+ this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
1498
+ }
1499
+ }
1500
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IconsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1501
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: IconsComponent, isStandalone: true, selector: "dc-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, usesOnChanges: true, ngImport: i0, template: `
1502
+ <span
1503
+ [innerHTML]="sanitizedIcon"
1504
+ [style.display]="'inline-flex'"
1505
+ [style.alignItems]="'center'"
1506
+ [style.justifyContent]="'center'"
1507
+ [style.width.px]="size()"
1508
+ [style.height.px]="size()"
1509
+ [style.color]="color()"></span>
1510
+ `, 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"] }); }
1511
+ }
1512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IconsComponent, decorators: [{
1513
+ type: Component,
1514
+ args: [{ selector: 'dc-icon', template: `
1515
+ <span
1516
+ [innerHTML]="sanitizedIcon"
1517
+ [style.display]="'inline-flex'"
1518
+ [style.alignItems]="'center'"
1519
+ [style.justifyContent]="'center'"
1520
+ [style.width.px]="size()"
1521
+ [style.height.px]="size()"
1522
+ [style.color]="color()"></span>
1523
+ `, standalone: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }]
1524
+ }], ctorParameters: () => [] });
1525
+
1526
+ class TextHighlighterComponent {
1527
+ constructor() {
1528
+ this.message = input.required(); // Or input.required<MessageAudio>() if always expected
1529
+ this.highlightedWords = signal([]); // Signal for highlighted words
1530
+ this.isPlaying = signal(false); // Signal for play state
1531
+ // Signals State computed from the input message
1532
+ this.iconState = computed(() => {
1533
+ if (this.isLoading()) {
1534
+ return 'isLoading';
1535
+ }
1536
+ else if (this.isPlaying()) {
1537
+ return 'isPlaying';
1538
+ }
1539
+ else if (this.message()?.audioUrl) {
1540
+ return 'playable';
1541
+ }
1542
+ else {
1543
+ return 'idle';
1544
+ }
1545
+ });
1546
+ this.isLoading = computed(() => this.message()?.isLoading);
1547
+ this.shouldPlayAudio = computed(() => !!this.message() && this.message()?.shouldPlayAudio);
1548
+ // Computed signal for message text
1549
+ this.messageText = computed(() => {
1550
+ console.log('Message text computed', this.message());
1551
+ const msg = this.message(); // Read the input signal
1552
+ return msg?.text || msg?.content || 'N/A';
1553
+ });
1554
+ this.classTag = computed(() => this.message()?.tag);
1555
+ // Computed signal for transcription availability
1556
+ this.hasTranscription = computed(() => {
1557
+ const hasTranscription = !!this.message()?.transcriptionTimestamps && this.message()?.transcriptionTimestamps.length > 0;
1558
+ return hasTranscription;
1559
+ });
1560
+ this.playAudio = output();
1561
+ this.audioCompleted = output();
1562
+ this.audioElement = null; // Audio element for playback
1563
+ this.destroy$ = new Subject(); // Cleanup localsubject
1564
+ // Inject services
1565
+ this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
1566
+ this.destroyRef = inject(DestroyRef);
1567
+ // Effect to react to message changes and re-initialize
1568
+ effect(() => {
1569
+ const currentMsg = this.message(); // Read the input signal
1570
+ console.log('Input message signal changed:', currentMsg);
1571
+ if (currentMsg) {
1572
+ // Re-run initialization logic whenever the message signal changes
1573
+ this.initializeBasedOnMessage(currentMsg);
1574
+ // Check if shouldPlayAudio flag is set
1575
+ if (currentMsg.shouldPlayAudio) {
1576
+ this.startAudioPlayback();
1577
+ }
1578
+ }
1579
+ else {
1580
+ // Handle case where message becomes undefined (cleanup?)
1581
+ this.cleanupAudio();
1582
+ this.highlightedWords.set([]);
1583
+ }
1584
+ // No cdr.markForCheck() needed here usually if template bindings use signals/computed
1585
+ });
1586
+ // Keep the effect for highlightedWords for now, might be redundant if template binds directly
1587
+ effect(() => {
1588
+ this.highlightedWords();
1589
+ this.cdr.markForCheck(); // Keep if template uses non-signal bindings for highlighted words
1590
+ });
1591
+ }
1592
+ /**
1593
+ * Track function for ngFor to improve performance
1594
+ */
1595
+ trackByIndex(index, item) {
1596
+ return index;
1597
+ }
1598
+ ngOnDestroy() {
1599
+ this.destroy$.next();
1600
+ this.destroy$.complete();
1601
+ this.cleanupAudio();
1602
+ }
1603
+ initializeBasedOnMessage(msg) {
1604
+ // Initialize or cleanup audio based on URL presence and change
1605
+ if (msg.audioUrl && (!this.audioElement || this.audioElement.src !== msg.audioUrl)) {
1606
+ this.initializeAudio(msg.audioUrl); // Pass URL
1607
+ }
1608
+ else if (!msg.audioUrl && this.audioElement) {
1609
+ this.cleanupAudio();
1610
+ }
1611
+ else if (!msg.audioUrl) {
1612
+ // No audio URL and no audio element - just log a warning instead of throwing an error
1613
+ console.warn('No audioUrl provided in the message. Audio playback will not be available.');
1614
+ }
1615
+ // Initialize words and sync based on transcription presence
1616
+ if (this.hasTranscription()) {
1617
+ // Use computed signal
1618
+ const timestamps = msg.transcriptionTimestamps || [];
1619
+ this.initializeHighlightedWords(timestamps); // Pass timestamps
1620
+ this.subcribeToAudioSync(timestamps); // Pass timestamps
1621
+ }
1622
+ else {
1623
+ this.subscribeToEndAudio();
1624
+ }
1625
+ }
1626
+ /**
1627
+ * Initialize the audio element and set up event listeners
1628
+ */
1629
+ initializeAudio(audioUrl) {
1630
+ // Clean up any existing audio element and listeners first
1631
+ this.cleanupAudio();
1632
+ this.audioElement = new Audio(audioUrl); // Use passed URL
1633
+ }
1634
+ /**
1635
+ * Initialize highlighted words from transcription timestamps
1636
+ */
1637
+ initializeHighlightedWords(timestamps) {
1638
+ const initialWords = timestamps.map((word, index) => ({
1639
+ word: word.word,
1640
+ index,
1641
+ isHighlighted: false,
1642
+ }));
1643
+ this.highlightedWords.set(initialWords);
1644
+ }
1645
+ /**
1646
+ * Set up audio synchronization with text
1647
+ */
1648
+ subcribeToAudioSync(timestamps) {
1649
+ // Accept timestamps
1650
+ if (!this.audioElement) {
1651
+ // Guard against missing audio element
1652
+ return;
1653
+ }
1654
+ // Important: Clean up previous listeners before setting up new ones
1655
+ this.destroy$.next(); // Signal previous subscriptions to complete
1656
+ // Listen to timeupdate events
1657
+ fromEvent(this.audioElement, 'timeupdate')
1658
+ .pipe(takeUntilDestroyed(this.destroyRef), // Use DestroyRef for component lifecycle
1659
+ takeUntil(this.destroy$), // Use manual subject for re-initialization cleanup
1660
+ map(() => this.audioElement?.currentTime || 0))
1661
+ .subscribe((currentTime) => {
1662
+ // Use the passed timestamps array
1663
+ const updatedWords = timestamps.map((word, index) => {
1664
+ const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
1665
+ return { word: word.word, index, isHighlighted };
1666
+ });
1667
+ this.highlightedWords.set(updatedWords);
1668
+ });
1669
+ // Listen to ended event for cleanup
1670
+ fromEvent(this.audioElement, 'ended')
1671
+ .pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
1672
+ .subscribe(() => {
1673
+ // Reset highlighting when audio ends
1674
+ const resetWords = this.highlightedWords().map((word) => ({
1675
+ // Read current words signal
1676
+ ...word,
1677
+ isHighlighted: false,
1678
+ }));
1679
+ this.highlightedWords.set(resetWords);
1680
+ // Set isPlaying to false when audio finishes
1681
+ this.isPlaying.set(false);
1682
+ // Emit audio completed event with the current message
1683
+ const currentMsg = this.message();
1684
+ if (currentMsg) {
1685
+ // Reset the shouldPlayAudio flag
1686
+ currentMsg.shouldPlayAudio = false;
1687
+ this.audioCompleted.emit(currentMsg);
1688
+ }
1689
+ });
1690
+ }
1691
+ subscribeToEndAudio() {
1692
+ if (!this.audioElement) {
1693
+ return;
1694
+ }
1695
+ fromEvent(this.audioElement, 'ended')
1696
+ .pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
1697
+ .subscribe(() => {
1698
+ // Set isPlaying to false when audio finishes
1699
+ this.isPlaying.set(false);
1700
+ // Emit audio completed event with the current message
1701
+ const currentMsg = this.message();
1702
+ if (currentMsg) {
1703
+ currentMsg.shouldPlayAudio = false;
1704
+ this.audioCompleted.emit(currentMsg);
1705
+ }
1706
+ });
1707
+ }
1708
+ /**
1709
+ * Clean up audio element and event listeners
1710
+ */
1711
+ cleanupAudio() {
1712
+ if (this.audioElement) {
1713
+ this.audioElement.pause();
1714
+ this.audioElement.removeAttribute('src'); // Use removeAttribute for better cleanup
1715
+ this.audioElement = null;
1716
+ this.destroy$.next(); // Ensure listeners tied to this audio instance are cleaned up
1717
+ // Reset highlighting immediately on cleanup
1718
+ const resetWords = this.highlightedWords().map((word) => ({
1719
+ ...word,
1720
+ isHighlighted: false,
1721
+ }));
1722
+ this.highlightedWords.set(resetWords);
1723
+ }
1724
+ }
1725
+ /**
1726
+ * Play or pause the audio
1727
+ */
1728
+ onPlayMessage() {
1729
+ console.log('Playing message audio', this.message().messageId);
1730
+ const currentMsg = this.message(); // Read signal
1731
+ if (this.audioElement) {
1732
+ if (this.audioElement.paused) {
1733
+ this.startAudioPlayback();
1734
+ }
1735
+ else {
1736
+ this.audioElement.pause();
1737
+ }
1738
+ }
1739
+ else if (currentMsg?.audioUrl) {
1740
+ // Initialization now happens via effect. If element doesn't exist yet,
1741
+ // the effect should create it. Clicking play might need to ensure init runs.
1742
+ // For simplicity, let's re-call initializeAudio here if needed,
1743
+ // though ideally the effect handles it.
1744
+ this.initializeAudio(currentMsg.audioUrl); // Ensure it's created if somehow missed
1745
+ // this.initializeBasedOnMessage(currentMsg);
1746
+ this.startAudioPlayback();
1747
+ }
1748
+ else if (currentMsg) {
1749
+ // If no audio URL is available, emit event to parent component
1750
+ this.playAudio.emit(currentMsg); // Emit the message value
1751
+ }
1752
+ }
1753
+ /**
1754
+ * Start audio playback and handle any setup needed
1755
+ */
1756
+ startAudioPlayback() {
1757
+ if (!this.audioElement)
1758
+ return;
1759
+ // Play the audio
1760
+ this.audioElement.play().catch((error) => {
1761
+ console.error('Error playing audio:', error);
1762
+ });
1763
+ this.isPlaying.set(true); // Set play state to true
1764
+ }
1765
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1766
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: TextHighlighterComponent, isStandalone: true, selector: "dc-text-highlighter", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <div> </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span> {{ messageText() }}</span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1767
+ }
1768
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, decorators: [{
1769
+ type: Component,
1770
+ args: [{ selector: 'dc-text-highlighter', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <div> </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }}&nbsp;</span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span> {{ messageText() }}</span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"] }]
1771
+ }], ctorParameters: () => [] });
1772
+
1773
+ class MessageOrchestratorComponent {
1774
+ constructor() {
1775
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
1776
+ this.conversationService = inject(ConversationService);
1777
+ this.messages = input.required();
1778
+ this.messageRole = input.required();
1779
+ this.messagesSignal = signal([]);
1780
+ this.playAudio = output();
1781
+ this.audioCompleted = output();
1782
+ // Audio queue management
1783
+ this.audioQueue = [];
1784
+ this.isGenerating = false;
1785
+ this.currentPlayingIndex = null;
1786
+ this.preGenerationInProgress = false;
1787
+ }
1788
+ ngOnInit() {
1789
+ if (this.messageRole() === ChatRole.Assistant) {
1790
+ console.log('MessageOrchestratorComponent initialized', this.messages());
1791
+ // Initialize the queue with all message indices
1792
+ this.initializeAudioQueue();
1793
+ // Start processing the queue - generate the first audio
1794
+ this.processNextInQueue();
1795
+ }
1796
+ else {
1797
+ // since user only have one message, just activate signal
1798
+ this.changeStates(0, this.messages()[0]);
1847
1799
  }
1848
1800
  }
1849
- // Add message to conversation
1850
- addMessage(message) {
1851
- this.messagesSignal.update((messages) => [...messages, message]);
1852
- }
1853
- // Process assistant message
1854
- processAssistantMessage(message, mutate = false) {
1855
- const conversationSettings = this.conversationSettingsSignal();
1856
- if (!conversationSettings) {
1857
- throw new Error('Conversation settings not initialized');
1801
+ // Initialize the queue with all message indices
1802
+ initializeAudioQueue() {
1803
+ const messages = this.messages();
1804
+ if (messages && messages.length > 0) {
1805
+ this.audioQueue = messages.map((_, index) => index);
1858
1806
  }
1859
- return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
1860
1807
  }
1861
- // Send current conversation to AI
1862
- async sendCurrentConversation() {
1863
- if (this.isDestroyedSignal()) {
1808
+ // Process the next message in the queue
1809
+ async processNextInQueue() {
1810
+ console.log('Processing next message in queue', this.audioQueue);
1811
+ if (this.audioQueue.length === 0 || this.isGenerating) {
1864
1812
  return;
1865
1813
  }
1866
- const messages = this.messagesSignal();
1867
- const conversationSettings = this.conversationSettingsSignal();
1868
- const agentCard = this.agentCardSignal();
1869
- if (!conversationSettings || !agentCard) {
1870
- throw new Error('Conversation not properly initialized');
1871
- }
1872
- if (messages.length > 31) {
1873
- // Safety limit to prevent infinite conversations
1814
+ // Get the next index from the queue
1815
+ const nextIndex = this.audioQueue.shift();
1816
+ // Start generating
1817
+ this.isGenerating = true;
1818
+ // Generate audio for the current message
1819
+ await this.generateAudioForIndex(nextIndex);
1820
+ // After generation, mark as not generating
1821
+ this.isGenerating = false;
1822
+ // If there's another message in the queue, pre-generate it
1823
+ this.preGenerateNextIfNeeded();
1824
+ }
1825
+ // Pre-generate the next audio if available
1826
+ async preGenerateNextIfNeeded() {
1827
+ if (this.audioQueue.length > 0 && !this.preGenerationInProgress) {
1828
+ const nextIndex = this.audioQueue[0]; // Peek at next index but don't remove
1829
+ // Mark pre-generation as in progress
1830
+ this.preGenerationInProgress = true;
1831
+ // Mark as loading but don't set shouldPlayAudio yet
1832
+ const messages = this.messages();
1833
+ const message = { ...messages[nextIndex], isLoading: true };
1834
+ this.changeStates(nextIndex, message);
1835
+ // Generate in background
1836
+ const messageAudio = await this.generateAudio(messages[nextIndex], null);
1837
+ messageAudio.isLoading = false;
1838
+ messageAudio.shouldPlayAudio = false; // Don't auto-play yet
1839
+ this.changeStates(nextIndex, messageAudio);
1840
+ // Reset pre-generation flag
1841
+ this.preGenerationInProgress = false;
1842
+ }
1843
+ }
1844
+ // Generate audio for a specific index
1845
+ async generateAudioForIndex(index) {
1846
+ const messages = this.messages();
1847
+ // Check if this message was already pre-generated (has audioUrl)
1848
+ if (messages[index].audioUrl) {
1849
+ // If it was pre-generated, just set it to play
1850
+ const messageAudio = { ...messages[index], shouldPlayAudio: true };
1851
+ this.changeStates(index, messageAudio);
1852
+ this.currentPlayingIndex = index;
1874
1853
  return;
1875
1854
  }
1876
- let conversationMessages = messages;
1877
- // Add last prompt if available
1878
- if (conversationSettings.last_prompt) {
1879
- conversationMessages = [
1880
- ...messages,
1881
- { content: conversationSettings.last_prompt, role: ChatRole.System },
1882
- ];
1883
- }
1884
- // Prepare conversation DTO
1885
- const conversation = {
1886
- messages: conversationMessages,
1887
- conversationType: conversationSettings.conversationType,
1888
- textEngine: conversationSettings.textEngine,
1889
- model: agentCard.model,
1890
- };
1891
- // Call AI service
1892
- const response = await this.agentCardService.callChatCompletion(conversation);
1893
- if (!response) {
1894
- console.error('No message returned from AI, is your service working?');
1895
- throw new Error('No message returned from AI');
1896
- }
1897
- // Process response
1898
- const newMessage = this.processAssistantMessage(response);
1899
- // Add to messages
1900
- this.addMessage(newMessage);
1901
- this.isThinkingSignal.set(false);
1902
- }
1903
- // Helper to get audio playback setting
1904
- getAudioPlaybackSetting() {
1905
- const agentCard = this.agentCardSignal();
1906
- return agentCard?.conversationSettings?.repeatRecording || false;
1855
+ // Otherwise generate it now
1856
+ const message = { ...messages[index], isLoading: true };
1857
+ this.changeStates(index, message);
1858
+ const messageAudio = await this.generateAudio(messages[index], null);
1859
+ messageAudio.isLoading = false;
1860
+ messageAudio.shouldPlayAudio = true;
1861
+ this.changeStates(index, messageAudio);
1862
+ this.currentPlayingIndex = index;
1863
+ }
1864
+ // Handle audio completion event from text-highlighter
1865
+ onAudioCompleted(message) {
1866
+ // Forward the event
1867
+ this.audioCompleted.emit(message);
1868
+ // Reset current playing index
1869
+ this.currentPlayingIndex = null;
1870
+ // If there are more messages in the queue, process the next one
1871
+ if (this.audioQueue.length > 0) {
1872
+ this.processNextInQueue();
1873
+ }
1874
+ }
1875
+ changeStates(index, messageAudio) {
1876
+ const messages = this.messages();
1877
+ messages[index] = { ...messages[index], ...messageAudio };
1878
+ this.messagesSignal.set([...messages]);
1907
1879
  }
1908
- // Reset conversation
1909
- async resetConversation(agentCard) {
1910
- if (agentCard) {
1911
- this.agentCardSignal.set(agentCard);
1880
+ async generateAudio(message, overwriteText = null) {
1881
+ try {
1882
+ const text = overwriteText || message.text || message.content;
1883
+ const ttsObject = buildObjectTTSRequest({ ...message, text }, { highlightWords: true });
1884
+ const speechAudio = await this.conversationService.getTTSFile(ttsObject);
1885
+ message = extractAudioAndTranscription(message, speechAudio);
1886
+ return message;
1912
1887
  }
1913
- // Clear messages
1914
- this.messagesSignal.set([]);
1915
- // Re-initialize with current agent card
1916
- const currentAgentCard = agentCard || this.agentCardSignal();
1917
- if (currentAgentCard) {
1918
- // Note: This would need the conversationBuilder to be injected or passed
1919
- // await this.initConversation(currentAgentCard, conversationBuilder);
1888
+ finally {
1889
+ // No longer emitting audioCompleted here as it's handled by the text-highlighter
1920
1890
  }
1921
1891
  }
1922
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ConversationService, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: MessageProcessingService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1923
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
1892
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1893
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: MessageOrchestratorComponent, isStandalone: true, selector: "dc-message-orchestrator", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, messageRole: { classPropertyName: "messageRole", publicName: "messageRole", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter [message]=\"message\" (playAudio)=\"playAudio.emit($event)\" (audioCompleted)=\"onAudioCompleted($event)\" />\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: TextHighlighterComponent, selector: "dc-text-highlighter", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1924
1894
  }
1925
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ConversationService, decorators: [{
1926
- type: Injectable,
1927
- args: [{
1928
- providedIn: 'root',
1929
- }]
1930
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
1931
- type: Inject,
1932
- args: [CONVERSATION_AI_TOKEN]
1933
- }] }, { type: MessageProcessingService }] });
1895
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, decorators: [{
1896
+ type: Component,
1897
+ args: [{ selector: 'dc-message-orchestrator', imports: [TextHighlighterComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter [message]=\"message\" (playAudio)=\"playAudio.emit($event)\" (audioCompleted)=\"onAudioCompleted($event)\" />\n}\n", styles: [":host{display:block}\n"] }]
1898
+ }] });
1899
+
1900
+ class ChatMessageComponent {
1901
+ constructor() {
1902
+ this.chatMessage = input.required();
1903
+ this.chatUserSettings = input(null);
1904
+ this.audioMessage = signal(null);
1905
+ // Computed properties for easier access to signal values
1906
+ this.hasMultiMessages = computed(() => !!this.chatMessage()?.multiMessages);
1907
+ this.multiMessages = computed(() => this.chatMessage()?.multiMessages || []);
1908
+ this.messageTranslation = computed(() => this.chatMessage()?.translation);
1909
+ this.isUserMessage = computed(() => this.chatMessage()?.role === ChatRole.User);
1910
+ // Field initializer for the effect - runs during component creation
1911
+ this.messageEffect = effect(() => {
1912
+ const message = this.chatMessage();
1913
+ if (!message)
1914
+ return;
1915
+ if (message.role === ChatRole.AssistantHelper) {
1916
+ return;
1917
+ }
1918
+ if (message.role === ChatRole.User) {
1919
+ this.audioMessage.set({ ...message });
1920
+ }
1921
+ else if (message.role === ChatRole.Assistant) {
1922
+ if (this.multiMessages().length > 0) {
1923
+ }
1924
+ else {
1925
+ this.audioMessage.set({ ...message });
1926
+ }
1927
+ }
1928
+ });
1929
+ }
1930
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1931
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: { classPropertyName: "chatMessage", publicName: "chatMessage", isSignal: true, isRequired: true, transformFunction: null }, chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-message-orchestrator [messages]=\"multiMessages()\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n } @else {\n <dc-message-orchestrator [messages]=\"[audioMessage()]\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-left:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px;overflow:hidden}.avatar-image{width:100%;height:100%;object-fit:cover}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageOrchestratorComponent, selector: "dc-message-orchestrator", inputs: ["messages", "messageRole"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1932
+ }
1933
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, decorators: [{
1934
+ type: Component,
1935
+ args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MessageOrchestratorComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-message-orchestrator [messages]=\"multiMessages()\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n } @else {\n <dc-message-orchestrator [messages]=\"[audioMessage()]\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-left:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px;overflow:hidden}.avatar-image{width:100%;height:100%;object-fit:cover}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"] }]
1936
+ }] });
1934
1937
 
1935
1938
  class ChatMessagesListComponent {
1936
1939
  constructor() {
1940
+ this.chatUserSettings = input.required();
1937
1941
  this.aiIcon = 'assets/default/ai.png';
1938
1942
  this.conversationService = inject(ConversationService);
1943
+ this.inputMessages = input.required();
1939
1944
  // Get messages and thinking state from the conversation service
1940
- this.messages = this.conversationService.getMessages();
1945
+ this.messages = computed(() => {
1946
+ // Get the actual array of messages from the signal by calling it as a function
1947
+ const allMessages = this.conversationService.getMessagesSignal()();
1948
+ return allMessages.filter((message) => message.role !== ChatRole.System);
1949
+ });
1941
1950
  this.isThinking = this.conversationService.isThinking();
1942
1951
  }
1943
1952
  // Track messages by their content and role for efficient rendering
1944
1953
  trackByMessage(index, message) {
1945
1954
  return `${message.role}-${index}-${message.content.substring(0, 20)}`;
1946
1955
  }
1947
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1948
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: "chatUserSettings", thinkingImg: "thinkingImg" }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message \n [chatMessage]=\"message\" \n [chatUserSettings]=\"chatUserSettings\">\n </dc-chat-message>\n }\n \n @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\">\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$3.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1956
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1957
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null }, inputMessages: { classPropertyName: "inputMessages", publicName: "inputMessages", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1949
1958
  }
1950
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
1959
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
1951
1960
  type: Component,
1952
- args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [CommonModule, ChatMessageComponent, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message \n [chatMessage]=\"message\" \n [chatUserSettings]=\"chatUserSettings\">\n </dc-chat-message>\n }\n \n @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\">\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"] }]
1953
- }], propDecorators: { chatUserSettings: [{
1954
- type: Input
1955
- }], thinkingImg: [{
1956
- type: Input
1957
- }] } });
1961
+ args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [ChatMessageComponent, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"] }]
1962
+ }] });
1958
1963
 
1959
1964
  const SpeedDescription = {
1960
1965
  1: 'Muy Lento',
@@ -1967,10 +1972,10 @@ class SpeedDescPipe {
1967
1972
  transform(speed) {
1968
1973
  return SpeedDescription[speed];
1969
1974
  }
1970
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SpeedDescPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1971
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: SpeedDescPipe, isStandalone: true, name: "speedDisplay" }); }
1975
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1976
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, isStandalone: true, name: "speedDisplay" }); }
1972
1977
  }
1973
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SpeedDescPipe, decorators: [{
1978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, decorators: [{
1974
1979
  type: Pipe,
1975
1980
  args: [{
1976
1981
  name: 'speedDisplay',
@@ -1987,10 +1992,10 @@ class TruncatePipe {
1987
1992
  }
1988
1993
  return value.substring(0, length) + suffix;
1989
1994
  }
1990
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1991
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: TruncatePipe, isStandalone: true, name: "truncate" }); }
1995
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1996
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, isStandalone: true, name: "truncate" }); }
1992
1997
  }
1993
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TruncatePipe, decorators: [{
1998
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, decorators: [{
1994
1999
  type: Pipe,
1995
2000
  args: [{
1996
2001
  name: 'truncate',
@@ -2012,9 +2017,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2012
2017
  * ```
2013
2018
  */
2014
2019
  class ProviderSelectorComponent {
2015
- constructor(conversationCardAIService, cdr) {
2016
- this.conversationCardAIService = conversationCardAIService;
2017
- this.cdr = cdr;
2020
+ constructor() {
2021
+ this.conversationCardAIService = inject(CONVERSATION_AI_TOKEN);
2022
+ this.cdr = inject(ChangeDetectorRef);
2018
2023
  /** Flag indicating whether the models are currently being loaded */
2019
2024
  this.isLoadingModels = true;
2020
2025
  /** Array of available models for the selected provider */
@@ -2045,26 +2050,23 @@ class ProviderSelectorComponent {
2045
2050
  });
2046
2051
  }
2047
2052
  }
2048
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ProviderSelectorComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2049
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ProviderSelectorComponent, isStandalone: true, selector: "dc-provider-selector", inputs: { parentForm: "parentForm" }, ngImport: i0, template: "<div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Providers:</b>\n <div style=\"display: flex; gap: 10px\" [formGroup]=\"parentForm\">\n <div class=\"space\">\n <p-radioButton value=\"groq\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Groq</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openai\" formControlName=\"provider\"></p-radioButton>\n <label>Open AI</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"google\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Google</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openrouter\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Open Router</label>\n </div>\n </div>\n\n <b>Modelo: </b>\n <span pTooltip=\"Modelo Seleccionado\">{{ parentForm.controls.modelName.value }}</span>\n @if (parentForm.controls.provider.value) { @if(isLoadingModels) {\n <p-skeleton height=\"200px\" width=\"100%\"></p-skeleton>\n } @else {\n <p-table [value]=\"modelnames\" stripedRows [size]=\"'small'\" [paginator]=\"true\" [rows]=\"12\" [formGroup]=\"parentForm\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th></th>\n <th>Name</th>\n <th>$$Prompt M</th>\n <th>$$Completion M</th>\n <th>$$Cost 90/10 M</th>\n <th>Created</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-model>\n <tr [pTooltip]=\"model.description | truncate : 200\" tooltipPosition=\"top\">\n <td><p-radioButton [value]=\"model.id\" formControlName=\"modelName\"></p-radioButton></td>\n <td>{{ model.name }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.completion * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 * 0.9 + +model.pricing?.completion * 1000000 * 0.1 | number : '1.2-2' }}</td>\n <td>{{ model.created * 1000 | date : 'dd/MM/yyyy' }}</td>\n </tr>\n </ng-template>\n </p-table>\n } }\n</div>\n", styles: [":host{display:block}.space{display:flex;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$2.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i3$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.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: i1$3.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 }); }
2053
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ProviderSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2054
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", 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: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i2$2.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: i3$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i4.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: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.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" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2050
2055
  }
2051
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ProviderSelectorComponent, decorators: [{
2056
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ProviderSelectorComponent, decorators: [{
2052
2057
  type: Component,
2053
- args: [{ selector: 'dc-provider-selector', standalone: true, imports: [CommonModule, ReactiveFormsModule, RadioButtonModule, TableModule, SkeletonModule, TooltipModule, TruncatePipe], changeDetection: ChangeDetectionStrategy.OnPush, 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"] }]
2054
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
2055
- type: Inject,
2056
- args: [CONVERSATION_AI_TOKEN]
2057
- }] }, { type: i0.ChangeDetectorRef }], propDecorators: { parentForm: [{
2058
+ args: [{ selector: 'dc-provider-selector', standalone: true, imports: [ReactiveFormsModule, RadioButtonModule, TableModule, SkeletonModule, TooltipModule, TruncatePipe, DatePipe, DecimalPipe], changeDetection: ChangeDetectionStrategy.OnPush, 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"] }]
2059
+ }], ctorParameters: () => [], propDecorators: { parentForm: [{
2058
2060
  type: Input
2059
2061
  }] } });
2060
2062
 
2061
2063
  class DCConversationUserChatSettingsComponent {
2062
- constructor(dialogRef, fb, conversationCardAIService) {
2063
- this.dialogRef = dialogRef;
2064
- this.fb = fb;
2065
- this.conversationCardAIService = conversationCardAIService;
2064
+ constructor() {
2065
+ this.dialogRef = inject(DynamicDialogRef);
2066
+ this.fb = inject(FormBuilder);
2067
+ this.conversationCardAIService = inject(CONVERSATION_AI_TOKEN);
2066
2068
  this.isLoadingModels = true;
2067
- this.showFeature = {
2069
+ this.showFeature = input({
2068
2070
  synthVoice: true,
2069
2071
  highlightWords: true,
2070
2072
  speed: true,
@@ -2072,8 +2074,8 @@ class DCConversationUserChatSettingsComponent {
2072
2074
  superHearing: true,
2073
2075
  fixGrammar: true,
2074
2076
  autoTranslate: true,
2075
- };
2076
- this.onSettingsChange = new EventEmitter();
2077
+ });
2078
+ this.onSettingsChange = output();
2077
2079
  this.textEngines = Object.values(TextEngines);
2078
2080
  this.voiceTTSOptions = Object.values(VoiceTTSOptions);
2079
2081
  this.textEngineOptions = TextEngineOptions;
@@ -2118,13 +2120,12 @@ class DCConversationUserChatSettingsComponent {
2118
2120
  this.dialogRef.close(this.form.value);
2119
2121
  }
2120
2122
  }
2121
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [{ token: i1$4.DynamicDialogRef }, { token: i1.FormBuilder }, { token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
2122
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCConversationUserChatSettingsComponent, isStandalone: true, selector: "dc-chat-settings-dialog", inputs: { showFeature: "showFeature" }, outputs: { onSettingsChange: "onSettingsChange" }, viewQueries: [{ propertyName: "tooltipRef", first: true, predicate: ["tooltipRef"], descendants: true }], ngImport: i0, template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$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$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: SpeedDescPipe, name: "speedDisplay" }, { kind: "ngmodule", type: RatingModule }, { kind: "component", type: i8.Rating, selector: "p-rating", inputs: ["disabled", "readonly", "stars", "iconOnClass", "iconOnStyle", "iconOffClass", "iconOffStyle", "autofocus"], outputs: ["onRate", "onCancel", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }] }); }
2123
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2124
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationUserChatSettingsComponent, isStandalone: true, selector: "dc-chat-settings-dialog", inputs: { showFeature: { classPropertyName: "showFeature", publicName: "showFeature", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSettingsChange: "onSettingsChange" }, viewQueries: [{ propertyName: "tooltipRef", first: true, predicate: ["tooltipRef"], descendants: true }], ngImport: i0, template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n @if (showFeature().synthVoice) {\n <div class=\"settings-section\">\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\n @if (showFeature().highlightWords) {\n <div class=\"settings-section\">\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\n @if (showFeature().speed) {\n <div class=\"settings-section\">\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\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\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\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\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\n @if (showFeature().superHearing) {\n <div class=\"settings-section\">\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\n @if (showFeature().fixGrammar) {\n <div class=\"settings-section\">\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\n @if (showFeature().autoTranslate) {\n <div class=\"settings-section\">\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\n @if (showFeature().autoTranslate) {\n <div class=\"voice-selection\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n }\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: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$3.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: i3$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: SliderModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i2$2.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: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: SpeedDescPipe, name: "speedDisplay" }, { kind: "ngmodule", type: RatingModule }, { kind: "component", type: i6$1.Rating, selector: "p-rating", inputs: ["disabled", "readonly", "stars", "iconOnClass", "iconOnStyle", "iconOffClass", "iconOffStyle", "autofocus"], outputs: ["onRate", "onCancel", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }] }); }
2123
2125
  }
2124
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
2126
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
2125
2127
  type: Component,
2126
2128
  args: [{ selector: 'dc-chat-settings-dialog', standalone: true, imports: [
2127
- CommonModule,
2128
2129
  ReactiveFormsModule,
2129
2130
  CheckboxModule,
2130
2131
  SliderModule,
@@ -2136,16 +2137,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2136
2137
  BadgeModule,
2137
2138
  SkeletonModule,
2138
2139
  TooltipModule,
2139
- ProviderSelectorComponent,
2140
- ], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"] }]
2141
- }], ctorParameters: () => [{ type: i1$4.DynamicDialogRef }, { type: i1.FormBuilder }, { type: AgentCardsAbstractService, decorators: [{
2142
- type: Inject,
2143
- args: [CONVERSATION_AI_TOKEN]
2144
- }] }], propDecorators: { showFeature: [{
2145
- type: Input
2146
- }], onSettingsChange: [{
2147
- type: Output
2148
- }], tooltipRef: [{
2140
+ ProviderSelectorComponent
2141
+ ], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n @if (showFeature().synthVoice) {\n <div class=\"settings-section\">\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\n @if (showFeature().highlightWords) {\n <div class=\"settings-section\">\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\n @if (showFeature().speed) {\n <div class=\"settings-section\">\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\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\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\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\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\n @if (showFeature().superHearing) {\n <div class=\"settings-section\">\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\n @if (showFeature().fixGrammar) {\n <div class=\"settings-section\">\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\n @if (showFeature().autoTranslate) {\n <div class=\"settings-section\">\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\n @if (showFeature().autoTranslate) {\n <div class=\"voice-selection\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n }\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"] }]
2142
+ }], ctorParameters: () => [], propDecorators: { tooltipRef: [{
2149
2143
  type: ViewChild,
2150
2144
  args: ['tooltipRef']
2151
2145
  }] } });
@@ -2163,179 +2157,31 @@ const DefaultEvaluatorAgentCard = {
2163
2157
  sources: [],
2164
2158
  };
2165
2159
 
2166
- function extractJsonFromResponse(content) {
2167
- const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
2168
- if (!jsonMatch)
2169
- return null;
2170
- try {
2171
- return JSON.parse(jsonMatch[0]);
2172
- }
2173
- catch (error) {
2174
- console.error('Error parsing JSON:', error);
2175
- return null;
2176
- }
2177
- }
2178
-
2179
- class EvaluationService {
2180
- constructor(agentCardService) {
2181
- this.agentCardService = agentCardService;
2182
- this.scoreSignal = signal(10);
2183
- this.evaluationResultSignal = signal(null);
2184
- }
2185
- // Get score as a signal
2186
- getScore() {
2187
- return this.scoreSignal;
2188
- }
2189
- // Get evaluation result as a signal
2190
- getEvaluationResult() {
2191
- return this.evaluationResultSignal;
2192
- }
2193
- // Evaluate conversation
2194
- async evaluateConversation(messages, evaluator) {
2195
- // Filter conversation to only include user and assistant messages
2196
- const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
2197
- // Check if there are any user messages to evaluate
2198
- const userMessages = conversationMessages.filter((message) => message.role === ChatRole.User);
2199
- if (userMessages.length <= 0) {
2200
- console.log('No messages to evaluate', conversationMessages);
2201
- return;
2202
- }
2203
- // Format conversation for evaluation
2204
- const conversationMessagesString = conversationMessages
2205
- .map((message) => `${message.role}: ${message.content}`)
2206
- .join('\n');
2207
- // Create evaluation prompt
2208
- const instructions = `
2209
- Please replay to this task:
2210
- ${evaluator.task}
2211
- This is the conversation history:
2212
- ${conversationMessagesString}
2213
- and give the response in next JSON format.
2214
- ${evaluator.expectedResponseType}
2215
- `;
2216
- // Send evaluation request
2217
- const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
2218
- const response = await this.agentCardService.callChatCompletion({ messages: evaluationMessages });
2219
- // Extract JSON from response
2220
- const jsonData = extractJsonFromResponse(response.content);
2221
- this.evaluationResultSignal.set(jsonData);
2222
- // Update score if available
2223
- if (jsonData.score) {
2224
- if (jsonData.score <= 3) {
2225
- this.updateScore(jsonData.score * 10);
2226
- }
2227
- }
2228
- }
2229
- // Update score with limits
2230
- updateScore(additionalScore) {
2231
- this.scoreSignal.update(currentScore => {
2232
- const newScore = currentScore + additionalScore;
2233
- return newScore > 100 ? 100 : newScore;
2234
- });
2235
- }
2236
- // Reset score
2237
- resetScore() {
2238
- this.scoreSignal.set(10);
2239
- }
2240
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: EvaluationService, deps: [{ token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
2241
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: EvaluationService, providedIn: 'root' }); }
2242
- }
2243
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: EvaluationService, decorators: [{
2244
- type: Injectable,
2245
- args: [{
2246
- providedIn: 'root',
2247
- }]
2248
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
2249
- type: Inject,
2250
- args: [CONVERSATION_AI_TOKEN]
2251
- }] }] });
2252
-
2253
2160
  class DCChatComponent {
2254
- constructor(agentCardService, toastService, conversationBuilder, dialogService, conversationService, evaluationService) {
2255
- this.agentCardService = agentCardService;
2256
- this.toastService = toastService;
2257
- this.conversationBuilder = conversationBuilder;
2258
- this.dialogService = dialogService;
2259
- this.conversationService = conversationService;
2260
- this.evaluationService = evaluationService;
2261
- this.evaluatorAgentCard = DefaultEvaluatorAgentCard;
2262
- this.parseDict = {};
2263
- this.sendMessage = new EventEmitter();
2161
+ constructor() {
2162
+ this.conversationBuilder = inject(DCConversationPromptBuilderService);
2163
+ this.dialogService = inject(DialogService);
2164
+ this.conversationService = inject(ConversationService);
2165
+ this.userDataExchange = inject(USER_DATA_EXCHANGE);
2166
+ this.chatUserSettings = this.userDataExchange.getUserChatSettings(); // Default to user data exchange
2167
+ // TODO: those are optional think if i need them,
2168
+ this.evaluatorAgentCard = input(DefaultEvaluatorAgentCard);
2169
+ this.parseDict = input({});
2170
+ this.sendMessage = output(); // notifies whatever happened inside the chat
2264
2171
  this.micSettings = { useWhisper: true, lang: 'en' };
2265
- this.imageUser = `assets/default/user.svg`;
2266
- this.thinkingImg = `assets/default/thinking.svg`;
2267
- this.aiIcon = `assets/default/ai.svg`;
2268
2172
  this.isInfoVisible = false;
2269
- this.isChatSettingsVisible = false;
2270
- this.isUserTalking = false;
2271
- this.isGettingTranscription = false;
2272
2173
  this.isAdmin = true;
2273
2174
  // Get reactive state from services
2274
- this.messages = this.conversationService.getMessages();
2275
- this.isThinking = this.conversationService.isThinking();
2276
- this.score = this.evaluationService.getScore();
2175
+ // public messages = this.conversationService.getMessages();
2176
+ this.messages = signal([]);
2277
2177
  }
2278
2178
  async ngOnInit() {
2279
- debugger;
2280
- // Get user settings if not provided
2281
- if (!this.chatUserSettings) {
2282
- this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
2283
- }
2284
- // Initialize conversation
2285
- await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict);
2179
+ this.conversationService.initConversation(this.agentCard);
2286
2180
  }
2287
2181
  ngOnDestroy() {
2288
2182
  // Mark conversation as destroyed to prevent async operations
2289
2183
  this.conversationService.setDestroyed(true);
2290
2184
  }
2291
- /**
2292
- * Handle user message from chat footer
2293
- */
2294
- async onUserMessage(message) {
2295
- if (this.isThinking()) {
2296
- return;
2297
- }
2298
- await this.conversationService.sendUserMessage(message);
2299
- // Evaluate conversation after sending message
2300
- this.evaluateConversation();
2301
- }
2302
- /**
2303
- * Handle microphone input finished
2304
- */
2305
- async onMicFinished(eventBlob) {
2306
- if (!(eventBlob instanceof Blob)) {
2307
- if (!this.chatUserSettings.superHearing) {
2308
- this.isUserTalking = false;
2309
- }
2310
- return;
2311
- }
2312
- // Check if audio is too small
2313
- if (eventBlob.size < 10000) {
2314
- return;
2315
- }
2316
- this.isUserTalking = false;
2317
- this.isGettingTranscription = true;
2318
- try {
2319
- // Get transcription from audio
2320
- const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, {
2321
- conversationId: this.agentCard._id,
2322
- });
2323
- // Create message with transcription
2324
- const message = {
2325
- content: transcription.text,
2326
- role: ChatRole.User,
2327
- transcriptionTimestamps: transcription.words,
2328
- };
2329
- message['audioUrl'] = URL.createObjectURL(eventBlob);
2330
- // Send message to conversation
2331
- await this.conversationService.sendUserMessage(message);
2332
- // Evaluate conversation
2333
- this.evaluateConversation();
2334
- }
2335
- finally {
2336
- this.isGettingTranscription = false;
2337
- }
2338
- }
2339
2185
  /**
2340
2186
  * Open chat settings dialog
2341
2187
  */
@@ -2349,7 +2195,7 @@ class DCChatComponent {
2349
2195
  closeOnEscape: true,
2350
2196
  })
2351
2197
  .onClose.subscribe(async () => {
2352
- this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
2198
+ // TODO: Que hacer cuando cambie las configuraciones del usario?, creo que si existe en exchange no necesito guardarlo y leer desde el servicio.
2353
2199
  });
2354
2200
  }
2355
2201
  /**
@@ -2367,35 +2213,16 @@ class DCChatComponent {
2367
2213
  }
2368
2214
  await this.ngOnInit();
2369
2215
  }
2370
- /**
2371
- * Evaluate conversation using evaluator agent
2372
- */
2373
- async evaluateConversation() {
2374
- const messages = this.messages();
2375
- await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard);
2376
- }
2377
- 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$4.DialogService }, { token: ConversationService }, { token: EvaluationService }], target: i0.ɵɵFactoryTarget.Component }); }
2378
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.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 class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [isAIThinking]=\"isThinking()\" [micSettings]=\"micSettings\" (sendMessage)=\"onUserMessage($event)\" (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <div class=\"score-container\" *ngIf=\"score() > 0\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:var(--surface-ground, #f8f9fa)}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "score", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "thinkingImg"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i6.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2216
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2217
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [inputMessages]=\"messages()\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [micSettings]=\"micSettings\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"> </dc-chat-footer>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | safeJson }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | safeJson }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}.chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "evaluatorAgentCard", "micSettings"], outputs: ["sendMessage", "textInputChanged"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "inputMessages"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$3.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "pipe", type: SafeJsonPipe, name: "safeJson" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2379
2218
  }
2380
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCChatComponent, decorators: [{
2219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, decorators: [{
2381
2220
  type: Component,
2382
- args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [isAIThinking]=\"isThinking()\" [micSettings]=\"micSettings\" (sendMessage)=\"onUserMessage($event)\" (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <div class=\"score-container\" *ngIf=\"score() > 0\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:var(--surface-ground, #f8f9fa)}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
2383
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
2384
- type: Inject,
2385
- args: [CONVERSATION_AI_TOKEN]
2386
- }] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
2387
- type: Inject,
2388
- args: [TOAST_ALERTS_TOKEN]
2389
- }] }, { type: DCConversationPromptBuilderService }, { type: i1$4.DialogService }, { type: ConversationService }, { type: EvaluationService }], propDecorators: { chatUserSettings: [{
2221
+ args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule, SafeJsonPipe], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [inputMessages]=\"messages()\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [micSettings]=\"micSettings\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"> </dc-chat-footer>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | safeJson }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | safeJson }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}.chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
2222
+ }], propDecorators: { chatUserSettings: [{
2390
2223
  type: Input
2391
2224
  }], agentCard: [{
2392
2225
  type: Input
2393
- }], evaluatorAgentCard: [{
2394
- type: Input
2395
- }], parseDict: [{
2396
- type: Input
2397
- }], sendMessage: [{
2398
- type: Output
2399
2226
  }] } });
2400
2227
 
2401
2228
  async function getCharacterData(file) {
@@ -2529,32 +2356,32 @@ function getFileBuffer(file) {
2529
2356
  }
2530
2357
 
2531
2358
  class PromptPreviewDialogComponent {
2532
- constructor(dynamicDialogConfig, dialogRef) {
2533
- this.dynamicDialogConfig = dynamicDialogConfig;
2534
- this.dialogRef = dialogRef;
2359
+ constructor() {
2360
+ this.dynamicDialogConfig = inject(DynamicDialogConfig);
2361
+ this.dialogRef = inject(DynamicDialogRef);
2535
2362
  this.data = this.dynamicDialogConfig.data;
2536
2363
  }
2537
2364
  close() {
2538
2365
  this.dialogRef.close();
2539
2366
  }
2540
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, deps: [{ token: i1$4.DynamicDialogConfig }, { token: i1$4.DynamicDialogRef }], target: i0.ɵɵFactoryTarget.Component }); }
2541
- 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 }] }); }
2367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PromptPreviewDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2368
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", 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: DialogModule }] }); }
2542
2369
  }
2543
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: PromptPreviewDialogComponent, decorators: [{
2370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PromptPreviewDialogComponent, decorators: [{
2544
2371
  type: Component,
2545
2372
  args: [{
2546
2373
  selector: 'dc-prompt-preview-dialog',
2547
2374
  standalone: true,
2548
- imports: [CommonModule, DialogModule],
2375
+ imports: [DialogModule],
2549
2376
  template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `,
2550
2377
  }]
2551
- }], ctorParameters: () => [{ type: i1$4.DynamicDialogConfig }, { type: i1$4.DynamicDialogRef }] });
2378
+ }], ctorParameters: () => [] });
2552
2379
 
2553
2380
  class TranslateDialogComponent {
2554
- constructor(fb, dialogRef, data) {
2555
- this.fb = fb;
2556
- this.dialogRef = dialogRef;
2557
- this.data = data;
2381
+ constructor() {
2382
+ this.fb = inject(FormBuilder);
2383
+ this.dialogRef = inject(DialogRef);
2384
+ this.data = inject(DIALOG_DATA);
2558
2385
  this.languages = Object.entries(LangCodeDescriptionEs).map(([code, description]) => ({
2559
2386
  code,
2560
2387
  description,
@@ -2570,17 +2397,19 @@ class TranslateDialogComponent {
2570
2397
  onConfirm() {
2571
2398
  this.dialogRef.close(this.form.value.targetLang);
2572
2399
  }
2573
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, deps: [{ token: i1.FormBuilder }, { token: i2$1.DialogRef }, { token: DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
2574
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: TranslateDialogComponent, isStandalone: true, selector: "dc-translate-dialog", ngImport: i0, template: `
2400
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslateDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2401
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: TranslateDialogComponent, isStandalone: true, selector: "dc-translate-dialog", ngImport: i0, template: `
2575
2402
  <div class="translate-dialog">
2576
2403
  <h4>Tu idioma actual es: {{ data.currentLang }}</h4>
2577
2404
  <h2>Selecciona el idioma al que quieres traducir</h2>
2578
2405
  <form [formGroup]="form">
2579
2406
  <select formControlName="targetLang">
2580
2407
  <option value="">Select language...</option>
2581
- <option *ngFor="let lang of languages" [value]="lang.code">
2582
- {{ lang.description }}
2583
- </option>
2408
+ @for (lang of languages; track lang) {
2409
+ <option [value]="lang.code">
2410
+ {{ lang.description }}
2411
+ </option>
2412
+ }
2584
2413
  </select>
2585
2414
  </form>
2586
2415
  <div class="actions">
@@ -2588,20 +2417,22 @@ class TranslateDialogComponent {
2588
2417
  <button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
2589
2418
  </div>
2590
2419
  </div>
2591
- `, isInline: true, styles: [".translate-dialog{padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 8px #00000026}.actions{margin-top:20px;display:flex;justify-content:flex-end;gap:10px}select{width:100%;padding:8px;margin-top:10px;border:1px solid #ccc;border-radius:4px}button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer}button:first-child{background:#f0f0f0}button:last-child{background:#007bff;color:#fff}button:disabled{background:#ccc;cursor:not-allowed}h2{margin:0 0 16px;color:#333}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
2420
+ `, 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: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
2592
2421
  }
2593
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: TranslateDialogComponent, decorators: [{
2422
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslateDialogComponent, decorators: [{
2594
2423
  type: Component,
2595
- args: [{ selector: 'dc-translate-dialog', standalone: true, imports: [CommonModule, ReactiveFormsModule], template: `
2424
+ args: [{ selector: 'dc-translate-dialog', standalone: true, imports: [ReactiveFormsModule], template: `
2596
2425
  <div class="translate-dialog">
2597
2426
  <h4>Tu idioma actual es: {{ data.currentLang }}</h4>
2598
2427
  <h2>Selecciona el idioma al que quieres traducir</h2>
2599
2428
  <form [formGroup]="form">
2600
2429
  <select formControlName="targetLang">
2601
2430
  <option value="">Select language...</option>
2602
- <option *ngFor="let lang of languages" [value]="lang.code">
2603
- {{ lang.description }}
2604
- </option>
2431
+ @for (lang of languages; track lang) {
2432
+ <option [value]="lang.code">
2433
+ {{ lang.description }}
2434
+ </option>
2435
+ }
2605
2436
  </select>
2606
2437
  </form>
2607
2438
  <div class="actions">
@@ -2609,11 +2440,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2609
2440
  <button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
2610
2441
  </div>
2611
2442
  </div>
2612
- `, 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"] }]
2613
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$1.DialogRef }, { type: undefined, decorators: [{
2614
- type: Inject,
2615
- args: [DIALOG_DATA]
2616
- }] }] });
2443
+ `, 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"] }]
2444
+ }], ctorParameters: () => [] });
2617
2445
 
2618
2446
  function markdownToHTML(markdownText) {
2619
2447
  // Convert italics-bold (***text***)
@@ -2630,10 +2458,10 @@ class SimpleMdToHtmlPipe {
2630
2458
  transform(text) {
2631
2459
  return markdownToHTML(text);
2632
2460
  }
2633
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SimpleMdToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2634
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: SimpleMdToHtmlPipe, isStandalone: true, name: "simpleMdToHtml" }); }
2461
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2462
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, isStandalone: true, name: "simpleMdToHtml" }); }
2635
2463
  }
2636
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: SimpleMdToHtmlPipe, decorators: [{
2464
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, decorators: [{
2637
2465
  type: Pipe,
2638
2466
  args: [{
2639
2467
  name: 'simpleMdToHtml',
@@ -2647,10 +2475,10 @@ class StartDivToHtmlPipe {
2647
2475
  text = text.replace(/\n/g, '<br>');
2648
2476
  return text;
2649
2477
  }
2650
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: StartDivToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2651
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: StartDivToHtmlPipe, isStandalone: true, name: "startDividerToHtml" }); }
2478
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2479
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, isStandalone: true, name: "startDividerToHtml" }); }
2652
2480
  }
2653
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: StartDivToHtmlPipe, decorators: [{
2481
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, decorators: [{
2654
2482
  type: Pipe,
2655
2483
  args: [{
2656
2484
  name: 'startDividerToHtml',
@@ -2663,10 +2491,10 @@ class MdToHtmlArrayPipe {
2663
2491
  const htmlArray = convertToHTML(text);
2664
2492
  return htmlArray.map((val) => val.content).join('');
2665
2493
  }
2666
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2667
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, isStandalone: true, name: "mdToHtmlArray" }); }
2494
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2495
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, isStandalone: true, name: "mdToHtmlArray" }); }
2668
2496
  }
2669
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MdToHtmlArrayPipe, decorators: [{
2497
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, decorators: [{
2670
2498
  type: Pipe,
2671
2499
  args: [{
2672
2500
  name: 'mdToHtmlArray',
@@ -2675,11 +2503,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
2675
2503
  }] });
2676
2504
 
2677
2505
  class AccountPlatformForm {
2678
- constructor(route, fb, router, toastService) {
2679
- this.route = route;
2680
- this.fb = fb;
2681
- this.router = router;
2682
- this.toastService = toastService;
2506
+ constructor() {
2507
+ this.route = inject(ActivatedRoute);
2508
+ this.fb = inject(FormBuilder);
2509
+ this.router = inject(Router);
2510
+ this.toastService = inject(TOAST_ALERTS_TOKEN);
2683
2511
  // Format the platform options for dropdown display
2684
2512
  this.platformOptions = Object.values(EAccountsPlatform).map((value) => ({
2685
2513
  label: value.charAt(0).toUpperCase() + value.slice(1), // Capitalize first letter
@@ -2734,16 +2562,13 @@ class AccountPlatformForm {
2734
2562
  this.toastService.warn(infoToast);
2735
2563
  }
2736
2564
  }
2737
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, deps: [{ token: i1$5.ActivatedRoute }, { token: i1.FormBuilder }, { token: i1$5.Router }, { token: TOAST_ALERTS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
2738
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i4$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 }); }
2565
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2566
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i3$2.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2739
2567
  }
2740
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AccountPlatformForm, decorators: [{
2568
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, decorators: [{
2741
2569
  type: Component,
2742
2570
  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"] }]
2743
- }], ctorParameters: () => [{ type: i1$5.ActivatedRoute }, { type: i1.FormBuilder }, { type: i1$5.Router }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
2744
- type: Inject,
2745
- args: [TOAST_ALERTS_TOKEN]
2746
- }] }], propDecorators: { formArray: [{
2571
+ }], ctorParameters: () => [], propDecorators: { formArray: [{
2747
2572
  type: Input
2748
2573
  }] } });
2749
2574
 
@@ -2756,16 +2581,16 @@ class DCAgentCardFormComponent {
2756
2581
  };
2757
2582
  return imageSettings;
2758
2583
  }
2759
- constructor(fb, storageService, agentCardService, cdr, router, activatedRoute, dialogService, promptBuilder, toastService) {
2760
- this.fb = fb;
2761
- this.storageService = storageService;
2762
- this.agentCardService = agentCardService;
2763
- this.cdr = cdr;
2764
- this.router = router;
2765
- this.activatedRoute = activatedRoute;
2766
- this.dialogService = dialogService;
2767
- this.promptBuilder = promptBuilder;
2768
- this.toastService = toastService;
2584
+ constructor() {
2585
+ this.fb = inject(FormBuilder);
2586
+ this.storageService = inject(MultiImagesStorageService);
2587
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
2588
+ this.cdr = inject(ChangeDetectorRef);
2589
+ this.router = inject(Router);
2590
+ this.activatedRoute = inject(ActivatedRoute);
2591
+ this.dialogService = inject(DialogService);
2592
+ this.promptBuilder = inject(DCConversationPromptBuilderService);
2593
+ this.toastService = inject(TOAST_ALERTS_TOKEN, { optional: true });
2769
2594
  // select options
2770
2595
  this.textEngines = Object.values(TextEngines);
2771
2596
  this.conversationOptions = ConversationTypeOptions;
@@ -2775,26 +2600,26 @@ class DCAgentCardFormComponent {
2775
2600
  this.languageOptions = Object.entries(LangCodeDescriptionEs).map(([value, label]) => ({ value, label }));
2776
2601
  this.agentCardId = this.activatedRoute.snapshot.paramMap.get('id');
2777
2602
  this.audioSpeedOptions = Object.values(AudioSpeed$1).map((speed) => ({ label: speed, value: speed }));
2778
- this.storageSettings = this.getSettings();
2779
- this.bannerImgSettings = {
2603
+ this.storageSettings = input(this.getSettings());
2604
+ this.bannerImgSettings = input({
2780
2605
  path: 'conversation-cards/' + this.agentCardId,
2781
2606
  fileName: null,
2782
2607
  cropSettings: { aspectRatio: AspectType.Rectangle, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 700 },
2783
- };
2784
- this.imageStorageSettings = {
2608
+ });
2609
+ this.imageStorageSettings = input({
2785
2610
  path: 'conversation-cards/' + this.agentCardId,
2786
2611
  fileName: null,
2787
2612
  cropSettings: { aspectRatio: AspectType.Vertical_9_16, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 500 },
2788
- };
2613
+ });
2789
2614
  this.stickerStorageSettings = {
2790
2615
  path: `conversation-cards/${this.agentCardId}/stickers`,
2791
2616
  fileName: null,
2792
2617
  cropSettings: { aspectRatio: AspectType.Square, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 400 },
2793
2618
  };
2794
- this.onImageLoaded = new EventEmitter();
2795
- this.onSave = new EventEmitter();
2796
- this.onGoDetails = new EventEmitter();
2797
- this.onTranslate = new EventEmitter();
2619
+ this.onImageLoaded = output();
2620
+ this.onSave = output();
2621
+ this.onGoDetails = output();
2622
+ this.onTranslate = output();
2798
2623
  this.markdownForm = this.fb.group({ seeMarkdown: [false] });
2799
2624
  // Type is IAgentCard
2800
2625
  this.form = this.fb.group({
@@ -2831,7 +2656,7 @@ class DCAgentCardFormComponent {
2831
2656
  ngOnInit() {
2832
2657
  this.form.controls.conversationSettings.controls.autoStart;
2833
2658
  console.log('form', this.form.value, this.conversationOptions);
2834
- this.imageSettings = this.storageSettings.cropSettings;
2659
+ this.imageSettings = this.storageSettings().cropSettings;
2835
2660
  this.loadConversationCard();
2836
2661
  this.markdownForm.get('checked')?.valueChanges.subscribe((value) => {
2837
2662
  this.markdownForm.patchValue({ seeMarkdown: value }, { emitEvent: false });
@@ -2846,7 +2671,7 @@ class DCAgentCardFormComponent {
2846
2671
  this.patchFormWithConversationData();
2847
2672
  this.handleAlternateGreetings();
2848
2673
  }
2849
- this.storageSettings.path = 'conversation-cards/' + this.agentCardId;
2674
+ this.storageSettings().path = 'conversation-cards/' + this.agentCardId;
2850
2675
  this.cdr.detectChanges();
2851
2676
  }
2852
2677
  catch (err) {
@@ -3164,13 +2989,12 @@ class DCAgentCardFormComponent {
3164
2989
  this.toastService.success({ title: 'Sticker removed', subtitle: 'Sticker was removed' });
3165
2990
  this.cdr.detectChanges();
3166
2991
  }
3167
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.MultiImagesStorageService }, { token: CONVERSATION_AI_TOKEN }, { token: i0.ChangeDetectorRef }, { token: i1$5.Router }, { token: i1$5.ActivatedRoute }, { token: i1$4.DialogService }, { token: DCConversationPromptBuilderService }, { token: TOAST_ALERTS_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
3168
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: "storageSettings", bannerImgSettings: "bannerImgSettings", imageStorageSettings: "imageStorageSettings" }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#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$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i7.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$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$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"] }] }); }
2992
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2993
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: { classPropertyName: "storageSettings", publicName: "storageSettings", isSignal: true, isRequired: false, transformFunction: null }, bannerImgSettings: { classPropertyName: "bannerImgSettings", publicName: "bannerImgSettings", isSignal: true, isRequired: false, transformFunction: null }, imageStorageSettings: { classPropertyName: "imageStorageSettings", publicName: "imageStorageSettings", isSignal: true, isRequired: false, transformFunction: null } }, 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 @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#x2716;</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$3.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: i6$2.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: i6.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: i8.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: i9.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: i10.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"] }] }); }
3169
2994
  }
3170
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
2995
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
3171
2996
  type: Component,
3172
2997
  args: [{ selector: 'dc-agent-form', standalone: true, providers: [DialogService], imports: [
3173
- CommonModule,
3174
2998
  ReactiveFormsModule,
3175
2999
  CropperComponentModal,
3176
3000
  OverlayModule,
@@ -3189,31 +3013,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
3189
3013
  DynamicDialogModule,
3190
3014
  PopoverModule,
3191
3015
  ProviderSelectorComponent,
3192
- AccountPlatformForm,
3193
- ], 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"] }]
3194
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.MultiImagesStorageService }, { type: AgentCardsAbstractService, decorators: [{
3195
- type: Inject,
3196
- args: [CONVERSATION_AI_TOKEN]
3197
- }] }, { type: i0.ChangeDetectorRef }, { type: i1$5.Router }, { type: i1$5.ActivatedRoute }, { type: i1$4.DialogService }, { type: DCConversationPromptBuilderService }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
3198
- type: Optional
3199
- }, {
3200
- type: Inject,
3201
- args: [TOAST_ALERTS_TOKEN]
3202
- }] }], propDecorators: { storageSettings: [{
3203
- type: Input
3204
- }], bannerImgSettings: [{
3205
- type: Input
3206
- }], imageStorageSettings: [{
3207
- type: Input
3208
- }], onImageLoaded: [{
3209
- type: Output
3210
- }], onSave: [{
3211
- type: Output
3212
- }], onGoDetails: [{
3213
- type: Output
3214
- }], onTranslate: [{
3215
- type: Output
3216
- }] } });
3016
+ AccountPlatformForm
3017
+ ], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#x2716;</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
3018
+ }], ctorParameters: () => [] });
3217
3019
 
3218
3020
  class DCConversationCardUIComponent {
3219
3021
  constructor() {
@@ -3222,36 +3024,31 @@ class DCConversationCardUIComponent {
3222
3024
  { label: 'Delete', icon: 'pi pi-trash', title: 'delete', severity: 'danger', command: () => this.onDelete() },
3223
3025
  { label: 'Select', icon: 'pi pi-check', title: 'select', severity: 'success', command: () => this.onDetails() },
3224
3026
  ];
3225
- this.showOptions = true;
3226
- this.onCardAction = new EventEmitter();
3027
+ this.card = input(undefined);
3028
+ this.showOptions = input(true);
3029
+ this.onCardAction = output();
3227
3030
  }
3228
3031
  ngOnInit() {
3229
- const name = this.card.characterCard.data.name;
3230
- const description = this.card.characterCard.data.description;
3231
- this.card.characterCard.data.description = description.replace(/{{char}}/g, name);
3032
+ const name = this.card().characterCard.data.name;
3033
+ const description = this.card().characterCard.data.description;
3034
+ this.card().characterCard.data.description = description.replace(/{{char}}/g, name);
3232
3035
  }
3233
3036
  onDetails() {
3234
- this.onCardAction.emit({ event: 'details', card: this.card });
3037
+ this.onCardAction.emit({ event: 'details', card: this.card() });
3235
3038
  }
3236
3039
  onEdit() {
3237
- this.onCardAction.emit({ event: 'edit', card: this.card });
3040
+ this.onCardAction.emit({ event: 'edit', card: this.card() });
3238
3041
  }
3239
3042
  onDelete() {
3240
- this.onCardAction.emit({ event: 'delete', card: this.card });
3043
+ this.onCardAction.emit({ event: 'delete', card: this.card() });
3241
3044
  }
3242
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3243
- 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 }); }
3045
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3046
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onCardAction: "onCardAction" }, ngImport: i0, template: "<p-card class=\"card-image\">\n @if(showOptions()) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card()?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card().title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: PopoverModule }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$5.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3244
3047
  }
3245
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
3048
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
3246
3049
  type: Component,
3247
- 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"] }]
3248
- }], propDecorators: { card: [{
3249
- type: Input
3250
- }], showOptions: [{
3251
- type: Input
3252
- }], onCardAction: [{
3253
- type: Output
3254
- }] } });
3050
+ 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"] }]
3051
+ }] });
3255
3052
 
3256
3053
  // This component contains a really avanced strategy to dinamically render Conversation Cards Details so every app can implement it with their own Style and Behavior
3257
3054
  // The trick is use NGFOR with ngComponentOutlet and pass input, problem is, is not possible to get Outpus events, Option one is use injector with ngComponentOutlet and pass context function to handle , but somehow it inject html and due to sanity im not able to see popups
@@ -3262,14 +3059,18 @@ const DefaultColumns = [
3262
3059
  { field: 'title', header: 'Title', type: 'text' },
3263
3060
  ];
3264
3061
  class AgentCardListComponent extends PaginationBase {
3265
- constructor(agentCardService, toastService, route, router, cdr) {
3062
+ constructor() {
3063
+ const route = inject(ActivatedRoute);
3064
+ const router = inject(Router);
3266
3065
  super(route, router);
3267
- this.agentCardService = agentCardService;
3268
- this.toastService = toastService;
3269
- this.cdr = cdr;
3066
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
3067
+ this.toastService = inject(TOAST_ALERTS_TOKEN);
3068
+ this.cdr = inject(ChangeDetectorRef);
3270
3069
  this.viewMode = 'cards';
3271
- this.showOptions = true;
3272
- this.gridLayout = true;
3070
+ this.customCardComponent = input(undefined);
3071
+ this.showOptions = input(true);
3072
+ this.gridLayout = input(true);
3073
+ this.getCustomButtons = input();
3273
3074
  this.agentCards = [];
3274
3075
  this.cardEventSubs = [];
3275
3076
  this.cardComponent = null;
@@ -3279,7 +3080,7 @@ class AgentCardListComponent extends PaginationBase {
3279
3080
  // this.buttonActions = DefaultActions;
3280
3081
  this.filterConfig.returnProps = { _id: 1, title: 1, assets: 1, description: 1, 'characterCard.data.description': 1, 'characterCard.data.name': 1 };
3281
3082
  this.loadConversationCards();
3282
- this.cardComponent = this.customCardComponent || DCConversationCardUIComponent;
3083
+ this.cardComponent = this.customCardComponent() || DCConversationCardUIComponent;
3283
3084
  }
3284
3085
  subscribeDinamicInstantToEvents() {
3285
3086
  // Clear previous cardEventSubs
@@ -3289,9 +3090,12 @@ class AgentCardListComponent extends PaginationBase {
3289
3090
  subscribeToCardEvents() {
3290
3091
  this.outlets.forEach((outlet) => {
3291
3092
  const instance = outlet.componentInstance;
3292
- this.cardEventSubs.push(instance.onCardAction.subscribe((action) => {
3293
- this.onCardAction({ action: action.event, item: action.card });
3294
- }));
3093
+ if (instance && instance.onCardAction) {
3094
+ const subscription = instance.onCardAction.subscribe((action) => {
3095
+ this.onCardAction({ action: action.event, item: action.card });
3096
+ });
3097
+ this.cardEventSubs.push(subscription);
3098
+ }
3295
3099
  });
3296
3100
  }
3297
3101
  clearcardEventSubs() {
@@ -3385,13 +3189,12 @@ class AgentCardListComponent extends PaginationBase {
3385
3189
  this.doAction(actionEvent); // handle by father.
3386
3190
  }
3387
3191
  }
3388
- 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$5.ActivatedRoute }, { token: i1$5.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3389
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: "viewMode", customCardComponent: "customCardComponent", showOptions: "showOptions", gridLayout: "gridLayout", getCustomButtons: "getCustomButtons" }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i3$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onFilterAction", "onChangeSort"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$3.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"] }] }); }
3192
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3193
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: false, isRequired: false, transformFunction: null }, customCardComponent: { classPropertyName: "customCardComponent", publicName: "customCardComponent", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null }, gridLayout: { classPropertyName: "gridLayout", publicName: "gridLayout", isSignal: true, isRequired: false, transformFunction: null }, getCustomButtons: { classPropertyName: "getCustomButtons", publicName: "getCustomButtons", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions()\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout() }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions() }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$4.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onFilterAction", "onChangeSort"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
3390
3194
  }
3391
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: AgentCardListComponent, decorators: [{
3195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, decorators: [{
3392
3196
  type: Component,
3393
3197
  args: [{ selector: 'dc-agent-card-lists', imports: [
3394
- CommonModule,
3395
3198
  NgComponentOutlet,
3396
3199
  PopoverModule,
3397
3200
  ButtonModule,
@@ -3400,22 +3203,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
3400
3203
  SkeletonModule,
3401
3204
  SpeedDialModule,
3402
3205
  QuickTableComponent,
3403
- ], 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"] }]
3404
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
3405
- type: Inject,
3406
- args: [CONVERSATION_AI_TOKEN]
3407
- }] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
3408
- type: Inject,
3409
- args: [TOAST_ALERTS_TOKEN]
3410
- }] }, { type: i1$5.ActivatedRoute }, { type: i1$5.Router }, { type: i0.ChangeDetectorRef }], propDecorators: { viewMode: [{
3411
- type: Input
3412
- }], customCardComponent: [{
3413
- type: Input
3414
- }], showOptions: [{
3415
- type: Input
3416
- }], gridLayout: [{
3417
- type: Input
3418
- }], getCustomButtons: [{
3206
+ CommonModule,
3207
+ ], 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"] }]
3208
+ }], ctorParameters: () => [], propDecorators: { viewMode: [{
3419
3209
  type: Input
3420
3210
  }], outlets: [{
3421
3211
  type: ViewChildren,
@@ -3436,10 +3226,10 @@ class ParseCardPipe {
3436
3226
  const result = this.builderConversation.applyReplacements(text, parseDict);
3437
3227
  return result;
3438
3228
  }
3439
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3440
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, isStandalone: true, name: "parseCard" }); }
3229
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3230
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, isStandalone: true, name: "parseCard" }); }
3441
3231
  }
3442
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ParseCardPipe, decorators: [{
3232
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, decorators: [{
3443
3233
  type: Pipe,
3444
3234
  args: [{
3445
3235
  name: 'parseCard',
@@ -3448,13 +3238,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
3448
3238
  }] });
3449
3239
 
3450
3240
  class DcAgentCardDetailsComponent {
3451
- constructor(agentCardService, route, cdr, userDataExchange) {
3452
- this.agentCardService = agentCardService;
3453
- this.route = route;
3454
- this.cdr = cdr;
3455
- this.userDataExchange = userDataExchange;
3241
+ constructor() {
3242
+ this.agentCardService = inject(CONVERSATION_AI_TOKEN);
3243
+ this.route = inject(ActivatedRoute);
3244
+ this.cdr = inject(ChangeDetectorRef);
3245
+ this.userDataExchange = inject(USER_DATA_EXCHANGE);
3456
3246
  this.agentCardId = '';
3457
- this.onStartConversation = new EventEmitter();
3247
+ this.onStartConversation = output();
3458
3248
  this.showInfoLayer = false;
3459
3249
  }
3460
3250
  async ngOnInit() {
@@ -3480,22 +3270,14 @@ class DcAgentCardDetailsComponent {
3480
3270
  this.showInfoLayer = !this.showInfoLayer;
3481
3271
  this.cdr.markForCheck();
3482
3272
  }
3483
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: i1$5.ActivatedRoute }, { token: i0.ChangeDetectorRef }, { token: USER_DATA_EXCHANGE }], target: i0.ɵɵFactoryTarget.Component }); }
3484
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { agentCardId: "agentCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n </div>\n </div>\n </div>\n </p-card>\n</div>\n\n<!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i3$2.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
3273
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3274
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { agentCardId: "agentCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\n <!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
3485
3275
  }
3486
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
3276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
3487
3277
  type: Component,
3488
- 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"] }]
3489
- }], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
3490
- type: Inject,
3491
- args: [CONVERSATION_AI_TOKEN]
3492
- }] }, { type: i1$5.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: UserDataExchangeAbstractService, decorators: [{
3493
- type: Inject,
3494
- args: [USER_DATA_EXCHANGE]
3495
- }] }], propDecorators: { agentCardId: [{
3278
+ args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [ButtonModule, CardModule, ParseCardPipe], template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\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"] }]
3279
+ }], ctorParameters: () => [], propDecorators: { agentCardId: [{
3496
3280
  type: Input
3497
- }], onStartConversation: [{
3498
- type: Output
3499
3281
  }] } });
3500
3282
 
3501
3283
  /*
@@ -3507,5 +3289,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
3507
3289
  * Generated bundle index. Do not edit.
3508
3290
  */
3509
3291
 
3510
- export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, ProviderSelectorComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, characterCardStringDataDefinition, defaultconvUserSettings, extractJsonFromResponse, provideChatAIService, provideUserDataExchange };
3292
+ export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, MessageOrchestratorComponent, ProviderSelectorComponent, TextEngineOptions, TextEngines, TextHighlighterComponent, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, buildObjectTTSRequest, characterCardStringDataDefinition, convertToHTML, defaultconvUserSettings, extractAudioAndTranscription, extractJsonFromResponse, markdownBasicToHTML, markdownToHTML$1 as markdownToHTML, markdownToHTML2, markdownToHtml, matchTranscription, provideChatAIService, provideUserDataExchange, removeAllEmojis, removeEmojis };
3511
3293
  //# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map