@dataclouder/ngx-agent-cards 0.0.87 → 0.0.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -7
- package/fesm2022/dataclouder-ngx-agent-cards.mjs +1314 -1292
- package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
- package/lib/components/chat-container/chat-container.component.d.ts +8 -22
- package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +16 -10
- package/lib/components/chat-container/chat-header/chat-header.component.d.ts +7 -8
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +16 -9
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +3 -2
- package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +17 -6
- package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +5 -3
- package/lib/components/chat-settings/dc-conversation-userchat-settings.component.d.ts +7 -8
- package/lib/components/dc-agent-card-details/dc-agent-card-details.component.d.ts +4 -6
- package/lib/components/dc-agent-card-lists/agent-card-default-ui/agent-card-default-ui.component.d.ts +5 -5
- package/lib/components/dc-agent-card-lists/dc-agent-card-lists.component.d.ts +9 -10
- package/lib/components/dc-agent-form/account-platform/account-platform-form.component.d.ts +3 -4
- package/lib/components/dc-agent-form/dc-agent-card-form.component.d.ts +16 -18
- package/lib/components/icons/icons.component.d.ts +7 -7
- package/lib/components/prompt-preview-dialog/prompt-preview-dialog.component.d.ts +3 -2
- package/lib/components/provider-selector/provider-selector.component.d.ts +3 -3
- package/lib/components/standalone-audio-text-sync/standalone-audio-text-sync.component.d.ts +62 -0
- package/lib/components/translate-dialog/translate-dialog.component.d.ts +3 -5
- package/lib/models/agent.models.d.ts +2 -0
- package/lib/models/conversation-ai.class.d.ts +2 -2
- package/lib/services/conversation.service.d.ts +2 -3
- package/lib/services/dc-conversation-builder.service.d.ts +2 -2
- package/lib/services/evaluation.service.d.ts +2 -2
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
- package/lib/components/audio-text-sync/audio-text-sync.component.d.ts +0 -42
- package/lib/components/chat-container/chat-messages-list/chat-message/message-content/message-content.component.d.ts +0 -24
|
@@ -1,66 +1,62 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable,
|
|
3
|
-
import * as i1$
|
|
4
|
-
import { CommonModule, NgComponentOutlet } from '@angular/common';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import * as i6 from 'primeng/dialog';
|
|
2
|
+
import { InjectionToken, Injectable, inject, input, output, Input, Component, signal, DestroyRef, computed, ChangeDetectorRef, effect, ChangeDetectionStrategy, Pipe, 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 i2$4 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
|
|
15
|
+
import * as i2$1 from 'primeng/button';
|
|
17
16
|
import { ButtonModule } from 'primeng/button';
|
|
18
|
-
import * as i1$1 from '@angular/platform-browser';
|
|
19
17
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
20
18
|
import { BehaviorSubject, Subject, fromEvent, filter } from 'rxjs';
|
|
21
19
|
import { takeUntil, map } from 'rxjs/operators';
|
|
22
|
-
import
|
|
20
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
21
|
+
import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
22
|
+
import * as i1$2 from 'primeng/skeleton';
|
|
23
23
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
24
|
-
import * as
|
|
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
|
|
27
|
+
import * as i2$2 from 'primeng/radiobutton';
|
|
28
28
|
import { RadioButtonModule } from 'primeng/radiobutton';
|
|
29
|
-
import * as
|
|
29
|
+
import * as i6$1 from 'primeng/rating';
|
|
30
30
|
import { RatingModule } from 'primeng/rating';
|
|
31
|
-
import * as
|
|
31
|
+
import * as i4 from 'primeng/table';
|
|
32
32
|
import { TableModule } from 'primeng/table';
|
|
33
33
|
import { BadgeModule } from 'primeng/badge';
|
|
34
|
-
import * as
|
|
34
|
+
import * as i6 from 'primeng/tooltip';
|
|
35
35
|
import { TooltipModule } from 'primeng/tooltip';
|
|
36
|
-
import * as
|
|
37
|
-
import
|
|
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
|
|
40
|
+
import * as i4$1 from 'primeng/inputtext';
|
|
42
41
|
import { InputTextModule } from 'primeng/inputtext';
|
|
43
|
-
import * as
|
|
42
|
+
import * as i6$2 from 'primeng/togglebutton';
|
|
44
43
|
import { ToggleButtonModule } from 'primeng/togglebutton';
|
|
45
|
-
import
|
|
46
|
-
import {
|
|
47
|
-
import * as
|
|
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
|
|
48
|
+
import * as i9 from 'primeng/select';
|
|
52
49
|
import { SelectModule } from 'primeng/select';
|
|
53
|
-
import * as
|
|
50
|
+
import * as i10 from 'primeng/popover';
|
|
54
51
|
import { PopoverModule } from 'primeng/popover';
|
|
55
|
-
import * as
|
|
52
|
+
import * as i2$5 from 'primeng/card';
|
|
56
53
|
import { CardModule } from 'primeng/card';
|
|
57
|
-
import * as
|
|
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$
|
|
61
|
-
import * as i3$3 from 'primeng/paginator';
|
|
57
|
+
import * as i1$3 from 'primeng/paginator';
|
|
62
58
|
import { PaginatorModule } from 'primeng/paginator';
|
|
63
|
-
import * as i2$
|
|
59
|
+
import * as i2$6 from 'primeng/speeddial';
|
|
64
60
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
65
61
|
|
|
66
62
|
const characterCardStringDataDefinition = `
|
|
@@ -512,10 +508,10 @@ class AudioService {
|
|
|
512
508
|
// const srcURL = await this.filesCacheService.getURLSrcFile(path);
|
|
513
509
|
this.playWithSrc(path);
|
|
514
510
|
}
|
|
515
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
516
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
511
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
512
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, providedIn: 'root' }); }
|
|
517
513
|
}
|
|
518
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
514
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, decorators: [{
|
|
519
515
|
type: Injectable,
|
|
520
516
|
args: [{
|
|
521
517
|
providedIn: 'root',
|
|
@@ -535,8 +531,8 @@ class UserDataExchangeAbstractService {
|
|
|
535
531
|
}
|
|
536
532
|
|
|
537
533
|
class DCConversationPromptBuilderService {
|
|
538
|
-
constructor(
|
|
539
|
-
this.userDataExchange =
|
|
534
|
+
constructor() {
|
|
535
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
540
536
|
}
|
|
541
537
|
// For chat conversation i need inital settings.
|
|
542
538
|
buildConversationSettings(agentCard, parseDict = null) {
|
|
@@ -678,27 +674,24 @@ class DCConversationPromptBuilderService {
|
|
|
678
674
|
};
|
|
679
675
|
return settings;
|
|
680
676
|
}
|
|
681
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
682
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
677
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
678
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, providedIn: 'root' }); }
|
|
683
679
|
}
|
|
684
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
680
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, decorators: [{
|
|
685
681
|
type: Injectable,
|
|
686
682
|
args: [{
|
|
687
683
|
providedIn: 'root',
|
|
688
684
|
}]
|
|
689
|
-
}], ctorParameters: () => [
|
|
690
|
-
type: Inject,
|
|
691
|
-
args: [USER_DATA_EXCHANGE]
|
|
692
|
-
}] }] });
|
|
685
|
+
}], ctorParameters: () => [] });
|
|
693
686
|
|
|
694
687
|
class ChatHeaderComponent {
|
|
695
|
-
constructor(
|
|
696
|
-
this.agentCardService =
|
|
697
|
-
this.isAdmin = false;
|
|
688
|
+
constructor() {
|
|
689
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
690
|
+
this.isAdmin = input(false);
|
|
698
691
|
this.alternativeConversation = [];
|
|
699
|
-
this.restartConversationEvent =
|
|
700
|
-
this.showInfoEvent =
|
|
701
|
-
this.settingsClickEvent =
|
|
692
|
+
this.restartConversationEvent = output();
|
|
693
|
+
this.showInfoEvent = output();
|
|
694
|
+
this.settingsClickEvent = output();
|
|
702
695
|
}
|
|
703
696
|
restartConversation(conversation = null) {
|
|
704
697
|
this.restartConversationEvent.emit(conversation);
|
|
@@ -723,87 +716,16 @@ class ChatHeaderComponent {
|
|
|
723
716
|
this.alternativeConversation = conversationCards.rows;
|
|
724
717
|
}
|
|
725
718
|
}
|
|
726
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
727
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
719
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
720
|
+
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
721
|
}
|
|
729
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
722
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatHeaderComponent, decorators: [{
|
|
730
723
|
type: Component,
|
|
731
|
-
args: [{ selector: 'dc-chat-header', standalone: true, imports: [
|
|
732
|
-
}], ctorParameters: () => [
|
|
733
|
-
type: Inject,
|
|
734
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
735
|
-
}] }], propDecorators: { isAdmin: [{
|
|
736
|
-
type: Input
|
|
737
|
-
}], alternativeConversation: [{
|
|
724
|
+
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"] }]
|
|
725
|
+
}], ctorParameters: () => [], propDecorators: { alternativeConversation: [{
|
|
738
726
|
type: Input
|
|
739
727
|
}], agentCard: [{
|
|
740
728
|
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
729
|
}] } });
|
|
808
730
|
|
|
809
731
|
function markdownToHtml(markdown) {
|
|
@@ -1007,954 +929,1229 @@ function removeAllEmojis(text) {
|
|
|
1007
929
|
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
930
|
}
|
|
1009
931
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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';
|
|
932
|
+
class MessageProcessingService {
|
|
933
|
+
constructor() { }
|
|
934
|
+
// Process message for display
|
|
935
|
+
processMessage(message, conversationSettings, mutate = false) {
|
|
936
|
+
const defaultVoice = this.getVoice(conversationSettings.voice);
|
|
937
|
+
let processedMessage;
|
|
938
|
+
if (mutate) {
|
|
939
|
+
// Modify the existing message object
|
|
940
|
+
message.voice = defaultVoice;
|
|
941
|
+
processedMessage = message;
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
// Create a new message object
|
|
945
|
+
processedMessage = {
|
|
946
|
+
content: message.content,
|
|
947
|
+
role: message.role,
|
|
948
|
+
voice: defaultVoice
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
// Process based on text engine
|
|
952
|
+
if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
|
|
953
|
+
this.processMultiMessages(processedMessage, conversationSettings);
|
|
954
|
+
}
|
|
955
|
+
else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
|
|
956
|
+
if (!conversationSettings.secondaryVoice) {
|
|
957
|
+
throw new Error('Secondary voice is required for SSML');
|
|
958
|
+
}
|
|
959
|
+
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
960
|
+
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
961
|
+
}
|
|
962
|
+
return processedMessage;
|
|
1053
963
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
964
|
+
// Process multi-messages from markdown
|
|
965
|
+
processMultiMessages(message, settings) {
|
|
966
|
+
// Convert markdown to HTML segments
|
|
967
|
+
const mdToHtml = convertToHTML(message.content);
|
|
968
|
+
// Map segments to multi-messages
|
|
969
|
+
message.multiMessages = mdToHtml.map((val) => {
|
|
970
|
+
// Get the narrator voice from conversation settings
|
|
971
|
+
const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
|
|
972
|
+
// Determine if this is italicized text (narrator)
|
|
973
|
+
const isItalics = val.tag === 'em';
|
|
974
|
+
const voice = isItalics ? narratorVoice : message.voice;
|
|
975
|
+
return {
|
|
976
|
+
voice,
|
|
977
|
+
content: val.content,
|
|
978
|
+
audioUrl: null,
|
|
979
|
+
audioPromise: null,
|
|
980
|
+
text: val.text,
|
|
981
|
+
tag: val.tag
|
|
982
|
+
};
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
// Replace italics with voice tags for SSML
|
|
986
|
+
subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
|
|
987
|
+
const regex = /\*(.*?)\*/g;
|
|
988
|
+
return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
|
|
989
|
+
}
|
|
990
|
+
// Get appropriate voice based on settings
|
|
991
|
+
getVoice(voice_value, targetLang = 'en') {
|
|
992
|
+
if ([null, '', 'random'].includes(voice_value)) {
|
|
993
|
+
const voiceCodes = VoiceTTSOptions.map((val) => val.id);
|
|
994
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
995
|
+
}
|
|
996
|
+
if (voice_value === 'randomMan') {
|
|
997
|
+
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
998
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
999
|
+
}
|
|
1000
|
+
else if (voice_value === 'randomWoman') {
|
|
1001
|
+
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
1002
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
|
|
1006
|
+
if (voice) {
|
|
1007
|
+
return voice.id;
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
console.error('Voice not found getting something random', voice_value);
|
|
1011
|
+
return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
|
|
1012
|
+
}
|
|
1058
1013
|
}
|
|
1059
1014
|
}
|
|
1060
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1061
|
-
static { this.ɵ
|
|
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"] }); }
|
|
1015
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1016
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, providedIn: 'root' }); }
|
|
1071
1017
|
}
|
|
1072
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1073
|
-
type:
|
|
1074
|
-
args: [{
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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
|
-
}] } });
|
|
1018
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, decorators: [{
|
|
1019
|
+
type: Injectable,
|
|
1020
|
+
args: [{
|
|
1021
|
+
providedIn: 'root',
|
|
1022
|
+
}]
|
|
1023
|
+
}], ctorParameters: () => [] });
|
|
1091
1024
|
|
|
1092
|
-
class
|
|
1025
|
+
class ConversationService {
|
|
1093
1026
|
constructor() {
|
|
1094
|
-
|
|
1095
|
-
this.
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1098
|
-
this.
|
|
1099
|
-
this.
|
|
1100
|
-
this.
|
|
1101
|
-
// Ensure cleanup when service is destroyed
|
|
1102
|
-
this.destroyRef.onDestroy(() => {
|
|
1103
|
-
this.stopAllSyncs();
|
|
1104
|
-
});
|
|
1027
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1028
|
+
this.messageProcessingService = inject(MessageProcessingService);
|
|
1029
|
+
this.messagesSignal = signal([]);
|
|
1030
|
+
this.isThinkingSignal = signal(false);
|
|
1031
|
+
this.conversationSettingsSignal = signal(null);
|
|
1032
|
+
this.agentCardSignal = signal(null);
|
|
1033
|
+
this.isDestroyedSignal = signal(false);
|
|
1105
1034
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1035
|
+
// Get messages as a signal
|
|
1036
|
+
getMessages() {
|
|
1037
|
+
return this.messagesSignal;
|
|
1038
|
+
}
|
|
1039
|
+
// Get thinking state as a signal
|
|
1040
|
+
isThinking() {
|
|
1041
|
+
return this.isThinkingSignal;
|
|
1042
|
+
}
|
|
1043
|
+
// Get conversation settings as a signal
|
|
1044
|
+
getConversationSettings() {
|
|
1045
|
+
return this.conversationSettingsSignal;
|
|
1046
|
+
}
|
|
1047
|
+
// Get agent card as a signal
|
|
1048
|
+
getAgentCard() {
|
|
1049
|
+
return this.agentCardSignal;
|
|
1050
|
+
}
|
|
1051
|
+
// Set destroyed state
|
|
1052
|
+
setDestroyed(value) {
|
|
1053
|
+
this.isDestroyedSignal.set(value);
|
|
1054
|
+
}
|
|
1055
|
+
// Initialize conversation
|
|
1056
|
+
async initConversation(agentCard, conversationBuilder, parseDict) {
|
|
1057
|
+
if (!agentCard?.conversationSettings) {
|
|
1058
|
+
throw new Error('Conversation settings are required');
|
|
1118
1059
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1060
|
+
this.agentCardSignal.set(agentCard);
|
|
1061
|
+
// Build conversation settings
|
|
1062
|
+
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1063
|
+
this.conversationSettingsSignal.set(conversationSettings);
|
|
1064
|
+
// Update agent card with conversation settings
|
|
1065
|
+
const updatedAgentCard = { ...agentCard, conversationSettings };
|
|
1066
|
+
this.agentCardSignal.set(updatedAgentCard);
|
|
1067
|
+
if (!conversationSettings.messages) {
|
|
1068
|
+
throw new Error('conversationSettings.messages is required in proper format to start conversation');
|
|
1069
|
+
}
|
|
1070
|
+
// Set initial messages
|
|
1071
|
+
this.messagesSignal.set(conversationSettings.messages);
|
|
1072
|
+
// Find first assistant message
|
|
1073
|
+
const firstAssistantMsg = conversationSettings.messages.find((message) => message.role === ChatRole.Assistant);
|
|
1074
|
+
if (firstAssistantMsg) {
|
|
1075
|
+
// Process the first assistant message
|
|
1076
|
+
this.processAssistantMessage(firstAssistantMsg, true);
|
|
1077
|
+
}
|
|
1078
|
+
else if (agentCard.conversationSettings.autoStart) {
|
|
1079
|
+
// Auto-start conversation if configured
|
|
1080
|
+
await this.sendCurrentConversation();
|
|
1121
1081
|
}
|
|
1122
|
-
// Create cleanup subject
|
|
1123
|
-
const cleanup$ = new Subject();
|
|
1124
|
-
this.cleanup$Map.set(messageId, cleanup$);
|
|
1125
|
-
// Store the active audio element
|
|
1126
|
-
this.activeAudioMap.set(messageId, audioElement);
|
|
1127
|
-
// Get the signal and subject for this message
|
|
1128
|
-
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1129
|
-
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1130
|
-
// Initialize the highlighted words state
|
|
1131
|
-
const initialWords = transcriptionTimestamps.map((word, index) => ({
|
|
1132
|
-
word: word.word,
|
|
1133
|
-
index,
|
|
1134
|
-
isHighlighted: false,
|
|
1135
|
-
}));
|
|
1136
|
-
// Update both signal and observable
|
|
1137
|
-
messageSignal.set(initialWords);
|
|
1138
|
-
messageSubject.next(initialWords);
|
|
1139
|
-
// Listen to timeupdate events
|
|
1140
|
-
fromEvent(audioElement, 'timeupdate')
|
|
1141
|
-
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$), map(() => audioElement.currentTime))
|
|
1142
|
-
.subscribe((currentTime) => {
|
|
1143
|
-
const updatedWords = transcriptionTimestamps.map((word, index) => {
|
|
1144
|
-
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1145
|
-
return {
|
|
1146
|
-
word: word.word,
|
|
1147
|
-
index,
|
|
1148
|
-
isHighlighted,
|
|
1149
|
-
};
|
|
1150
|
-
});
|
|
1151
|
-
// Update both signal and observable for this message
|
|
1152
|
-
messageSignal.set(updatedWords);
|
|
1153
|
-
messageSubject.next(updatedWords);
|
|
1154
|
-
});
|
|
1155
|
-
// Listen to ended event for cleanup
|
|
1156
|
-
fromEvent(audioElement, 'ended')
|
|
1157
|
-
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$))
|
|
1158
|
-
.subscribe(() => {
|
|
1159
|
-
// Reset highlighting when audio ends
|
|
1160
|
-
const resetWords = initialWords.map((word) => ({
|
|
1161
|
-
...word,
|
|
1162
|
-
isHighlighted: false,
|
|
1163
|
-
}));
|
|
1164
|
-
messageSignal.set(resetWords);
|
|
1165
|
-
messageSubject.next(resetWords);
|
|
1166
|
-
});
|
|
1167
1082
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1083
|
+
// Send user message
|
|
1084
|
+
async sendUserMessage(message) {
|
|
1085
|
+
if (this.isThinkingSignal()) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
// Add message to conversation
|
|
1089
|
+
this.addMessage(message);
|
|
1090
|
+
// Set thinking state
|
|
1091
|
+
this.isThinkingSignal.set(true);
|
|
1092
|
+
try {
|
|
1093
|
+
if (message.audioUrl && this.getAudioPlaybackSetting()) {
|
|
1094
|
+
// Wait for audio to finish playing before sending response
|
|
1095
|
+
await new Promise((resolve) => {
|
|
1096
|
+
if (message.audioHtml) {
|
|
1097
|
+
message.audioHtml.addEventListener('ended', () => {
|
|
1098
|
+
resolve();
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
resolve();
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1190
1105
|
}
|
|
1106
|
+
// Send to AI service
|
|
1107
|
+
await this.sendCurrentConversation();
|
|
1191
1108
|
}
|
|
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);
|
|
1109
|
+
finally {
|
|
1110
|
+
this.isThinkingSignal.set(false);
|
|
1200
1111
|
}
|
|
1201
|
-
// Clear all maps
|
|
1202
|
-
this.highlightedWordsSignalMap.clear();
|
|
1203
|
-
this.highlightedWords$Map.clear();
|
|
1204
|
-
this.cleanup$Map.clear();
|
|
1205
|
-
this.activeAudioMap.clear();
|
|
1206
1112
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1113
|
+
// Add message to conversation
|
|
1114
|
+
addMessage(message) {
|
|
1115
|
+
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1116
|
+
}
|
|
1117
|
+
// Process assistant message
|
|
1118
|
+
processAssistantMessage(message, mutate = false) {
|
|
1119
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1120
|
+
if (!conversationSettings) {
|
|
1121
|
+
throw new Error('Conversation settings not initialized');
|
|
1215
1122
|
}
|
|
1216
|
-
return this.
|
|
1123
|
+
return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
|
|
1217
1124
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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([]));
|
|
1125
|
+
// Send current conversation to AI
|
|
1126
|
+
async sendCurrentConversation() {
|
|
1127
|
+
if (this.isDestroyedSignal()) {
|
|
1128
|
+
return;
|
|
1226
1129
|
}
|
|
1227
|
-
|
|
1130
|
+
const messages = this.messagesSignal();
|
|
1131
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1132
|
+
const agentCard = this.agentCardSignal();
|
|
1133
|
+
if (!conversationSettings || !agentCard) {
|
|
1134
|
+
throw new Error('Conversation not properly initialized');
|
|
1135
|
+
}
|
|
1136
|
+
if (messages.length > 31) {
|
|
1137
|
+
// Safety limit to prevent infinite conversations
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
let conversationMessages = messages;
|
|
1141
|
+
// Add last prompt if available
|
|
1142
|
+
if (conversationSettings.last_prompt) {
|
|
1143
|
+
conversationMessages = [
|
|
1144
|
+
...messages,
|
|
1145
|
+
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1146
|
+
];
|
|
1147
|
+
}
|
|
1148
|
+
// Prepare conversation DTO
|
|
1149
|
+
const conversation = {
|
|
1150
|
+
messages: conversationMessages,
|
|
1151
|
+
conversationType: conversationSettings.conversationType,
|
|
1152
|
+
textEngine: conversationSettings.textEngine,
|
|
1153
|
+
model: agentCard.model,
|
|
1154
|
+
};
|
|
1155
|
+
// Call AI service
|
|
1156
|
+
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
1157
|
+
if (!response) {
|
|
1158
|
+
console.error('No message returned from AI, is your service working?');
|
|
1159
|
+
throw new Error('No message returned from AI');
|
|
1160
|
+
}
|
|
1161
|
+
// Process response
|
|
1162
|
+
const newMessage = this.processAssistantMessage(response);
|
|
1163
|
+
// Add to messages
|
|
1164
|
+
this.addMessage(newMessage);
|
|
1165
|
+
this.isThinkingSignal.set(false);
|
|
1228
1166
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
*/
|
|
1234
|
-
isWordHighlighted(messageId, index) {
|
|
1235
|
-
const messageSignal = this.getHighlightedWordsSignal(messageId);
|
|
1236
|
-
return messageSignal().some((word) => word.index === index && word.isHighlighted);
|
|
1167
|
+
// Helper to get audio playback setting
|
|
1168
|
+
getAudioPlaybackSetting() {
|
|
1169
|
+
const agentCard = this.agentCardSignal();
|
|
1170
|
+
return agentCard?.conversationSettings?.repeatRecording || false;
|
|
1237
1171
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1172
|
+
// Reset conversation
|
|
1173
|
+
async resetConversation(agentCard) {
|
|
1174
|
+
if (agentCard) {
|
|
1175
|
+
this.agentCardSignal.set(agentCard);
|
|
1176
|
+
}
|
|
1177
|
+
// Clear messages
|
|
1178
|
+
this.messagesSignal.set([]);
|
|
1179
|
+
// Re-initialize with current agent card
|
|
1180
|
+
const currentAgentCard = agentCard || this.agentCardSignal();
|
|
1181
|
+
if (currentAgentCard) {
|
|
1182
|
+
// Note: This would need the conversationBuilder to be injected or passed
|
|
1183
|
+
// await this.initConversation(currentAgentCard, conversationBuilder);
|
|
1184
|
+
}
|
|
1245
1185
|
}
|
|
1246
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1247
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
1186
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1187
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
|
|
1248
1188
|
}
|
|
1249
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1189
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, decorators: [{
|
|
1250
1190
|
type: Injectable,
|
|
1251
1191
|
args: [{
|
|
1252
1192
|
providedIn: 'root',
|
|
1253
1193
|
}]
|
|
1254
1194
|
}], ctorParameters: () => [] });
|
|
1255
1195
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
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;
|
|
1196
|
+
function extractJsonFromResponse(content) {
|
|
1197
|
+
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
1198
|
+
if (!jsonMatch)
|
|
1199
|
+
return null;
|
|
1200
|
+
try {
|
|
1201
|
+
return JSON.parse(jsonMatch[0]);
|
|
1286
1202
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
get messageText() {
|
|
1291
|
-
return this.message.content || this.message.text;
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
console.error('Error parsing JSON:', error);
|
|
1205
|
+
return null;
|
|
1292
1206
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
class EvaluationService {
|
|
1210
|
+
constructor() {
|
|
1211
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1212
|
+
this.scoreSignal = signal(10);
|
|
1213
|
+
this.evaluationResultSignal = signal(null);
|
|
1300
1214
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
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
|
-
}
|
|
1215
|
+
// Get score as a signal
|
|
1216
|
+
getScore() {
|
|
1217
|
+
return this.scoreSignal;
|
|
1334
1218
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
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}`;
|
|
1219
|
+
// Get evaluation result as a signal
|
|
1220
|
+
getEvaluationResult() {
|
|
1221
|
+
return this.evaluationResultSignal;
|
|
1343
1222
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1223
|
+
// Evaluate conversation
|
|
1224
|
+
async evaluateConversation(messages, evaluator) {
|
|
1225
|
+
// Filter conversation to only include user and assistant messages
|
|
1226
|
+
const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
1227
|
+
// Check if there are any user messages to evaluate
|
|
1228
|
+
const userMessages = conversationMessages.filter((message) => message.role === ChatRole.User);
|
|
1229
|
+
if (userMessages.length <= 0) {
|
|
1230
|
+
console.log('No messages to evaluate', conversationMessages);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
// Format conversation for evaluation
|
|
1234
|
+
const conversationMessagesString = conversationMessages
|
|
1235
|
+
.map((message) => `${message.role}: ${message.content}`)
|
|
1236
|
+
.join('\n');
|
|
1237
|
+
// Create evaluation prompt
|
|
1238
|
+
const instructions = `
|
|
1239
|
+
Please replay to this task:
|
|
1240
|
+
${evaluator.task}
|
|
1241
|
+
This is the conversation history:
|
|
1242
|
+
${conversationMessagesString}
|
|
1243
|
+
and give the response in next JSON format.
|
|
1244
|
+
${evaluator.expectedResponseType}
|
|
1245
|
+
`;
|
|
1246
|
+
// Send evaluation request
|
|
1247
|
+
const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
|
|
1248
|
+
const response = await this.agentCardService.callChatCompletion({ messages: evaluationMessages });
|
|
1249
|
+
// Extract JSON from response
|
|
1250
|
+
const jsonData = extractJsonFromResponse(response.content);
|
|
1251
|
+
this.evaluationResultSignal.set(jsonData);
|
|
1252
|
+
// Update score if available
|
|
1253
|
+
if (jsonData.score) {
|
|
1254
|
+
if (jsonData.score <= 3) {
|
|
1255
|
+
this.updateScore(jsonData.score * 10);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// Update score with limits
|
|
1260
|
+
updateScore(additionalScore) {
|
|
1261
|
+
this.scoreSignal.update(currentScore => {
|
|
1262
|
+
const newScore = currentScore + additionalScore;
|
|
1263
|
+
return newScore > 100 ? 100 : newScore;
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
// Reset score
|
|
1267
|
+
resetScore() {
|
|
1268
|
+
this.scoreSignal.set(10);
|
|
1349
1269
|
}
|
|
1350
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1351
|
-
static { this.ɵ
|
|
1270
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1271
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, providedIn: 'root' }); }
|
|
1352
1272
|
}
|
|
1353
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1354
|
-
type:
|
|
1355
|
-
args: [{
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
type: Input
|
|
1360
|
-
}], playAudio: [{
|
|
1361
|
-
type: Output
|
|
1362
|
-
}] } });
|
|
1273
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, decorators: [{
|
|
1274
|
+
type: Injectable,
|
|
1275
|
+
args: [{
|
|
1276
|
+
providedIn: 'root',
|
|
1277
|
+
}]
|
|
1278
|
+
}], ctorParameters: () => [] });
|
|
1363
1279
|
|
|
1364
|
-
class
|
|
1280
|
+
class ChatFooterComponent {
|
|
1365
1281
|
constructor() {
|
|
1366
|
-
this.
|
|
1367
|
-
this.
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1282
|
+
this.conversationService = inject(ConversationService);
|
|
1283
|
+
this.evaluationService = inject(EvaluationService);
|
|
1284
|
+
this.isAIThinking = input(false);
|
|
1285
|
+
this.evaluatorAgentCard = input();
|
|
1286
|
+
this.micSettings = input({ useWhisper: true, lang: 'en' });
|
|
1287
|
+
this.sendMessage = output();
|
|
1288
|
+
this.textInputChanged = output();
|
|
1289
|
+
this.micFinishedEvent = output();
|
|
1290
|
+
this.chatInputControl = new FormControl();
|
|
1291
|
+
// Get score from evaluation service
|
|
1292
|
+
this.score = this.evaluationService.getScore();
|
|
1374
1293
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1294
|
+
/**
|
|
1295
|
+
* Sets the input text in the textarea
|
|
1296
|
+
* @param text The text to set
|
|
1297
|
+
*/
|
|
1298
|
+
setInputText(text) {
|
|
1299
|
+
this.chatInputControl.setValue(text);
|
|
1300
|
+
this.textInputChanged.emit(text);
|
|
1377
1301
|
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1302
|
+
/**
|
|
1303
|
+
* Handles the mic finished event
|
|
1304
|
+
* @param eventBlob The blob event from the mic component
|
|
1305
|
+
*/
|
|
1306
|
+
micFinished(eventBlob) {
|
|
1307
|
+
this.micFinishedEvent.emit(eventBlob);
|
|
1380
1308
|
}
|
|
1381
1309
|
/**
|
|
1382
|
-
*
|
|
1383
|
-
* @param index The current item's index
|
|
1384
|
-
* @param item The current item
|
|
1310
|
+
* Sends the user message
|
|
1385
1311
|
*/
|
|
1386
|
-
|
|
1387
|
-
|
|
1312
|
+
async sendUserMessage() {
|
|
1313
|
+
if (this.isAIThinking() || !this.chatInputControl.value) {
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const text = this.chatInputControl.value;
|
|
1317
|
+
const message = {
|
|
1318
|
+
content: text,
|
|
1319
|
+
role: ChatRole.User,
|
|
1320
|
+
};
|
|
1321
|
+
// Emit the message for parent components that need it
|
|
1322
|
+
this.sendMessage.emit(message);
|
|
1323
|
+
// Clear the input field
|
|
1324
|
+
this.chatInputControl.setValue('');
|
|
1325
|
+
// Send the user message to the conversation service
|
|
1326
|
+
await this.conversationService.sendUserMessage(message);
|
|
1327
|
+
// Evaluate conversation after sending message
|
|
1328
|
+
this.evaluateConversation();
|
|
1388
1329
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1330
|
+
/**
|
|
1331
|
+
* Evaluate conversation using evaluator agent
|
|
1332
|
+
*/
|
|
1333
|
+
async evaluateConversation() {
|
|
1334
|
+
const messages = this.conversationService.getMessages()();
|
|
1335
|
+
if (this.evaluatorAgentCard()) {
|
|
1336
|
+
await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard());
|
|
1337
|
+
}
|
|
1391
1338
|
}
|
|
1392
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1393
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
1339
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1340
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: { classPropertyName: "isAIThinking", publicName: "isAIThinking", isSignal: true, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, micSettings: { classPropertyName: "micSettings", publicName: "micSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged", micFinishedEvent: "micFinishedEvent" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
|
|
1394
1341
|
}
|
|
1395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
1396
1343
|
type: Component,
|
|
1397
|
-
args: [{ selector: 'dc-
|
|
1398
|
-
}]
|
|
1399
|
-
type: Input
|
|
1400
|
-
}], isLoading: [{
|
|
1401
|
-
type: Input
|
|
1402
|
-
}], playAudio: [{
|
|
1403
|
-
type: Output
|
|
1404
|
-
}] } });
|
|
1344
|
+
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
|
|
1345
|
+
}] });
|
|
1405
1346
|
|
|
1406
|
-
class
|
|
1347
|
+
class AudioTextSyncService {
|
|
1407
1348
|
constructor() {
|
|
1408
|
-
|
|
1409
|
-
this.
|
|
1349
|
+
// Maps to store message-specific signals and observables
|
|
1350
|
+
this.highlightedWordsSignalMap = new Map();
|
|
1351
|
+
this.highlightedWords$Map = new Map();
|
|
1352
|
+
// Maps for cleanup and active audio elements
|
|
1353
|
+
this.cleanup$Map = new Map();
|
|
1354
|
+
this.activeAudioMap = new Map();
|
|
1355
|
+
this.destroyRef = inject(DestroyRef);
|
|
1356
|
+
// Ensure cleanup when service is destroyed
|
|
1357
|
+
this.destroyRef.onDestroy(() => {
|
|
1358
|
+
this.stopAllSyncs();
|
|
1359
|
+
});
|
|
1410
1360
|
}
|
|
1411
1361
|
/**
|
|
1412
|
-
*
|
|
1413
|
-
* @param
|
|
1362
|
+
* Synchronizes audio playback with text transcription
|
|
1363
|
+
* @param audioElement The audio element to sync with
|
|
1364
|
+
* @param transcriptionTimestamps Array of word timestamps
|
|
1365
|
+
* @param messageId Unique identifier for the message
|
|
1414
1366
|
*/
|
|
1415
|
-
|
|
1416
|
-
//
|
|
1417
|
-
|
|
1367
|
+
syncAudioWithText(audioElement, transcriptionTimestamps, messageId) {
|
|
1368
|
+
// Stop any existing sync for this message
|
|
1369
|
+
this.stopSync(messageId);
|
|
1370
|
+
// Create new signal and subject for this message if they don't exist
|
|
1371
|
+
if (!this.highlightedWordsSignalMap.has(messageId)) {
|
|
1372
|
+
this.highlightedWordsSignalMap.set(messageId, signal([]));
|
|
1373
|
+
}
|
|
1374
|
+
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1375
|
+
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1376
|
+
}
|
|
1377
|
+
// Create cleanup subject
|
|
1378
|
+
const cleanup$ = new Subject();
|
|
1379
|
+
this.cleanup$Map.set(messageId, cleanup$);
|
|
1380
|
+
// Store the active audio element
|
|
1381
|
+
this.activeAudioMap.set(messageId, audioElement);
|
|
1382
|
+
// Get the signal and subject for this message
|
|
1383
|
+
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1384
|
+
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1385
|
+
// Initialize the highlighted words state
|
|
1386
|
+
const initialWords = transcriptionTimestamps.map((word, index) => ({
|
|
1387
|
+
word: word.word,
|
|
1388
|
+
index,
|
|
1389
|
+
isHighlighted: false,
|
|
1390
|
+
}));
|
|
1391
|
+
// Update both signal and observable
|
|
1392
|
+
messageSignal.set(initialWords);
|
|
1393
|
+
messageSubject.next(initialWords);
|
|
1394
|
+
// Listen to timeupdate events
|
|
1395
|
+
fromEvent(audioElement, 'timeupdate')
|
|
1396
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$), map(() => audioElement.currentTime))
|
|
1397
|
+
.subscribe((currentTime) => {
|
|
1398
|
+
const updatedWords = transcriptionTimestamps.map((word, index) => {
|
|
1399
|
+
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1400
|
+
return {
|
|
1401
|
+
word: word.word,
|
|
1402
|
+
index,
|
|
1403
|
+
isHighlighted,
|
|
1404
|
+
};
|
|
1405
|
+
});
|
|
1406
|
+
// Update both signal and observable for this message
|
|
1407
|
+
messageSignal.set(updatedWords);
|
|
1408
|
+
messageSubject.next(updatedWords);
|
|
1409
|
+
});
|
|
1410
|
+
// Listen to ended event for cleanup
|
|
1411
|
+
fromEvent(audioElement, 'ended')
|
|
1412
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$))
|
|
1413
|
+
.subscribe(() => {
|
|
1414
|
+
// Reset highlighting when audio ends
|
|
1415
|
+
const resetWords = initialWords.map((word) => ({
|
|
1416
|
+
...word,
|
|
1417
|
+
isHighlighted: false,
|
|
1418
|
+
}));
|
|
1419
|
+
messageSignal.set(resetWords);
|
|
1420
|
+
messageSubject.next(resetWords);
|
|
1421
|
+
});
|
|
1418
1422
|
}
|
|
1419
1423
|
/**
|
|
1420
|
-
*
|
|
1421
|
-
* @param
|
|
1424
|
+
* Stops the sync for a specific message and cleans up resources
|
|
1425
|
+
* @param messageId The ID of the message to stop syncing
|
|
1422
1426
|
*/
|
|
1423
|
-
|
|
1424
|
-
this.
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1427
|
+
stopSync(messageId) {
|
|
1428
|
+
if (this.activeAudioMap.has(messageId)) {
|
|
1429
|
+
// Get the cleanup subject for this message
|
|
1430
|
+
const cleanup$ = this.cleanup$Map.get(messageId);
|
|
1431
|
+
if (cleanup$) {
|
|
1432
|
+
cleanup$.next();
|
|
1433
|
+
cleanup$.complete();
|
|
1434
|
+
this.cleanup$Map.delete(messageId);
|
|
1435
|
+
}
|
|
1436
|
+
this.activeAudioMap.delete(messageId);
|
|
1437
|
+
// Reset state for this message
|
|
1438
|
+
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1439
|
+
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1440
|
+
if (messageSignal) {
|
|
1441
|
+
messageSignal.set([]);
|
|
1442
|
+
}
|
|
1443
|
+
if (messageSubject) {
|
|
1444
|
+
messageSubject.next([]);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1428
1447
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
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);
|
|
1448
|
+
/**
|
|
1449
|
+
* Stops all syncs and cleans up all resources
|
|
1450
|
+
*/
|
|
1451
|
+
stopAllSyncs() {
|
|
1452
|
+
// Get all message IDs and stop each sync
|
|
1453
|
+
for (const messageId of this.activeAudioMap.keys()) {
|
|
1454
|
+
this.stopSync(messageId);
|
|
1451
1455
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1456
|
+
// Clear all maps
|
|
1457
|
+
this.highlightedWordsSignalMap.clear();
|
|
1458
|
+
this.highlightedWords$Map.clear();
|
|
1459
|
+
this.cleanup$Map.clear();
|
|
1460
|
+
this.activeAudioMap.clear();
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Returns the highlighted words signal for a specific message
|
|
1464
|
+
* @param messageId The ID of the message
|
|
1465
|
+
*/
|
|
1466
|
+
getHighlightedWordsSignal(messageId) {
|
|
1467
|
+
// Create a new signal for this message if it doesn't exist
|
|
1468
|
+
if (!this.highlightedWordsSignalMap.has(messageId)) {
|
|
1469
|
+
this.highlightedWordsSignalMap.set(messageId, signal([]));
|
|
1454
1470
|
}
|
|
1471
|
+
return this.highlightedWordsSignalMap.get(messageId);
|
|
1455
1472
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
if (
|
|
1463
|
-
|
|
1464
|
-
result.push({
|
|
1465
|
-
word: word,
|
|
1466
|
-
start: Number(matchedObj.start.toFixed(3)),
|
|
1467
|
-
end: Number(matchedObj.end.toFixed(3)),
|
|
1468
|
-
});
|
|
1469
|
-
currentTime = matchedObj.end;
|
|
1470
|
-
}
|
|
1471
|
-
else {
|
|
1472
|
-
result.push({
|
|
1473
|
-
word: word,
|
|
1474
|
-
start: Number(currentTime.toFixed(3)),
|
|
1475
|
-
end: Number(currentTime.toFixed(3)),
|
|
1476
|
-
});
|
|
1473
|
+
/**
|
|
1474
|
+
* Returns the highlighted words observable for a specific message
|
|
1475
|
+
* @param messageId The ID of the message
|
|
1476
|
+
*/
|
|
1477
|
+
getHighlightedWords$(messageId) {
|
|
1478
|
+
// Create a new subject for this message if it doesn't exist
|
|
1479
|
+
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1480
|
+
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1477
1481
|
}
|
|
1482
|
+
return this.highlightedWords$Map.get(messageId).asObservable();
|
|
1478
1483
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1484
|
+
/**
|
|
1485
|
+
* Checks if a word at a specific index is currently highlighted for a specific message
|
|
1486
|
+
* @param messageId The ID of the message
|
|
1487
|
+
* @param index The index of the word to check
|
|
1488
|
+
*/
|
|
1489
|
+
isWordHighlighted(messageId, index) {
|
|
1490
|
+
const messageSignal = this.getHighlightedWordsSignal(messageId);
|
|
1491
|
+
return messageSignal().some((word) => word.index === index && word.isHighlighted);
|
|
1486
1492
|
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Returns an observable that emits true when a word at a specific index is highlighted for a specific message
|
|
1495
|
+
* @param messageId The ID of the message
|
|
1496
|
+
* @param index The index of the word to observe
|
|
1497
|
+
*/
|
|
1498
|
+
isWordHighlighted$(messageId, index) {
|
|
1499
|
+
return this.getHighlightedWords$(messageId).pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
|
|
1500
|
+
}
|
|
1501
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1502
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, providedIn: 'root' }); }
|
|
1487
1503
|
}
|
|
1504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, decorators: [{
|
|
1505
|
+
type: Injectable,
|
|
1506
|
+
args: [{
|
|
1507
|
+
providedIn: 'root',
|
|
1508
|
+
}]
|
|
1509
|
+
}], ctorParameters: () => [] });
|
|
1488
1510
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1511
|
+
const ICONS = {
|
|
1512
|
+
chat: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
1513
|
+
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
|
|
1514
|
+
</svg>`,
|
|
1515
|
+
play: `<svg
|
|
1516
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1517
|
+
width="16"
|
|
1518
|
+
height="16"
|
|
1519
|
+
viewBox="0 0 24 24"
|
|
1520
|
+
fill="none"
|
|
1521
|
+
stroke="#263042"
|
|
1522
|
+
stroke-width="2"
|
|
1523
|
+
stroke-linecap="round"
|
|
1524
|
+
stroke-linejoin="round">
|
|
1525
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
1526
|
+
<polygon points="10 8 16 12 10 16 10 8"></polygon>
|
|
1527
|
+
</svg>`,
|
|
1528
|
+
loading: `<svg
|
|
1529
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1530
|
+
width="16"
|
|
1531
|
+
height="16"
|
|
1532
|
+
viewBox="0 0 24 24"
|
|
1533
|
+
fill="none"
|
|
1534
|
+
stroke="#263042"
|
|
1535
|
+
stroke-width="2"
|
|
1536
|
+
stroke-linecap="round"
|
|
1537
|
+
stroke-linejoin="round">
|
|
1538
|
+
<line x1="12" y1="2" x2="12" y2="6"></line>
|
|
1539
|
+
<line x1="12" y1="18" x2="12" y2="22"></line>
|
|
1540
|
+
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
|
|
1541
|
+
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
|
|
1542
|
+
<line x1="2" y1="12" x2="6" y2="12"></line>
|
|
1543
|
+
<line x1="18" y1="12" x2="22" y2="12"></line>
|
|
1544
|
+
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
|
|
1545
|
+
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
|
|
1546
|
+
</svg>`,
|
|
1547
|
+
};
|
|
1548
|
+
|
|
1549
|
+
class IconsComponent {
|
|
1550
|
+
constructor() {
|
|
1551
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1552
|
+
this.name = input.required();
|
|
1553
|
+
this.size = input(14);
|
|
1554
|
+
this.color = input('currentColor');
|
|
1497
1555
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
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
|
-
}
|
|
1556
|
+
ngOnChanges(changes) {
|
|
1557
|
+
if (changes['name']) {
|
|
1558
|
+
const svg = ICONS[this.name()] || '';
|
|
1559
|
+
this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
|
|
1509
1560
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1561
|
+
}
|
|
1562
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IconsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1563
|
+
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: `
|
|
1564
|
+
<span
|
|
1565
|
+
[innerHTML]="sanitizedIcon"
|
|
1566
|
+
[style.display]="'inline-flex'"
|
|
1567
|
+
[style.alignItems]="'center'"
|
|
1568
|
+
[style.justifyContent]="'center'"
|
|
1569
|
+
[style.width.px]="size()"
|
|
1570
|
+
[style.height.px]="size()"
|
|
1571
|
+
[style.color]="color()"></span>
|
|
1572
|
+
`, 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"] }); }
|
|
1573
|
+
}
|
|
1574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IconsComponent, decorators: [{
|
|
1575
|
+
type: Component,
|
|
1576
|
+
args: [{ selector: 'dc-icon', template: `
|
|
1577
|
+
<span
|
|
1578
|
+
[innerHTML]="sanitizedIcon"
|
|
1579
|
+
[style.display]="'inline-flex'"
|
|
1580
|
+
[style.alignItems]="'center'"
|
|
1581
|
+
[style.justifyContent]="'center'"
|
|
1582
|
+
[style.width.px]="size()"
|
|
1583
|
+
[style.height.px]="size()"
|
|
1584
|
+
[style.color]="color()"></span>
|
|
1585
|
+
`, standalone: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }]
|
|
1586
|
+
}], ctorParameters: () => [] });
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Standalone component for audio-text synchronization
|
|
1590
|
+
* This component can work independently from the chat component
|
|
1591
|
+
*/
|
|
1592
|
+
class StandaloneAudioTextSyncComponent {
|
|
1593
|
+
constructor() {
|
|
1594
|
+
this.message = input(undefined); // Or input.required<MessageAudio>() if always expected
|
|
1595
|
+
this.highlightedWords = signal([]); // Signal for highlighted words
|
|
1596
|
+
this.isLoading = computed(() => this.message()?.isLoading);
|
|
1597
|
+
this.shouldPlayAudio = computed(() => {
|
|
1598
|
+
debugger;
|
|
1599
|
+
const shouldPlay = !!this.message() && this.message()?.shouldPlayAudio;
|
|
1600
|
+
return shouldPlay;
|
|
1601
|
+
});
|
|
1602
|
+
this.playAudio = output();
|
|
1603
|
+
this.audioCompleted = output();
|
|
1604
|
+
this.audioElement = null; // Audio element for playback
|
|
1605
|
+
this.destroy$ = new Subject(); // Cleanup subject
|
|
1606
|
+
// Inject services
|
|
1607
|
+
this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
|
|
1608
|
+
this.destroyRef = inject(DestroyRef);
|
|
1609
|
+
// Computed signal for message text
|
|
1610
|
+
this.messageText = computed(() => {
|
|
1611
|
+
const msg = this.message(); // Read the input signal
|
|
1612
|
+
return msg?.content || msg?.text || '';
|
|
1613
|
+
});
|
|
1614
|
+
// Computed signal for transcription availability
|
|
1615
|
+
this.hasTranscription = computed(() => {
|
|
1616
|
+
const hasTranscription = !!this.message()?.transcriptionTimestamps && this.message()?.transcriptionTimestamps.length > 0;
|
|
1617
|
+
return hasTranscription;
|
|
1618
|
+
});
|
|
1619
|
+
// Effect to react to message changes and re-initialize
|
|
1620
|
+
effect(() => {
|
|
1621
|
+
debugger;
|
|
1622
|
+
const currentMsg = this.message(); // Read the input signal
|
|
1623
|
+
console.log('Input message signal changed:', currentMsg);
|
|
1624
|
+
if (currentMsg) {
|
|
1625
|
+
// Re-run initialization logic whenever the message signal changes
|
|
1626
|
+
this.initializeBasedOnMessage(currentMsg);
|
|
1627
|
+
// Check if shouldPlayAudio flag is set
|
|
1628
|
+
if (currentMsg.shouldPlayAudio) {
|
|
1629
|
+
this.startAudioPlayback();
|
|
1630
|
+
}
|
|
1513
1631
|
}
|
|
1514
1632
|
else {
|
|
1515
|
-
|
|
1633
|
+
// Handle case where message becomes undefined (cleanup?)
|
|
1634
|
+
this.cleanupAudio();
|
|
1635
|
+
this.highlightedWords.set([]);
|
|
1516
1636
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
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,
|
|
1637
|
+
// No cdr.markForCheck() needed here usually if template bindings use signals/computed
|
|
1638
|
+
});
|
|
1639
|
+
// Keep the effect for highlightedWords for now, might be redundant if template binds directly
|
|
1640
|
+
effect(() => {
|
|
1641
|
+
this.highlightedWords();
|
|
1642
|
+
this.cdr.markForCheck(); // Keep if template uses non-signal bindings for highlighted words
|
|
1546
1643
|
});
|
|
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
1644
|
}
|
|
1561
1645
|
/**
|
|
1562
|
-
*
|
|
1563
|
-
* Uses message content/text and a timestamp to ensure uniqueness
|
|
1646
|
+
* Track function for ngFor to improve performance
|
|
1564
1647
|
*/
|
|
1565
|
-
|
|
1566
|
-
|
|
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();
|
|
1648
|
+
trackByIndex(index, item) {
|
|
1649
|
+
return index;
|
|
1624
1650
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1651
|
+
ngOnDestroy() {
|
|
1652
|
+
this.destroy$.next();
|
|
1653
|
+
this.destroy$.complete();
|
|
1654
|
+
this.cleanupAudio();
|
|
1655
|
+
}
|
|
1656
|
+
// This method is called by the effect when the message signal changes
|
|
1657
|
+
initializeBasedOnMessage(msg) {
|
|
1658
|
+
// Initialize or cleanup audio based on URL presence and change
|
|
1659
|
+
if (msg.audioUrl && (!this.audioElement || this.audioElement.src !== msg.audioUrl)) {
|
|
1660
|
+
this.initializeAudio(msg.audioUrl); // Pass URL
|
|
1661
|
+
}
|
|
1662
|
+
else if (!msg.audioUrl && this.audioElement) {
|
|
1663
|
+
// Cleanup if URL removed and element exists
|
|
1664
|
+
this.cleanupAudio();
|
|
1665
|
+
}
|
|
1666
|
+
// Initialize words and sync based on transcription presence
|
|
1667
|
+
if (this.hasTranscription()) {
|
|
1668
|
+
// Use computed signal
|
|
1669
|
+
const timestamps = msg.transcriptionTimestamps || [];
|
|
1670
|
+
this.initializeHighlightedWords(timestamps); // Pass timestamps
|
|
1671
|
+
// Ensure audio sync setup happens if audio element exists
|
|
1672
|
+
if (this.audioElement) {
|
|
1673
|
+
this.setupAudioSync(timestamps); // Pass timestamps
|
|
1636
1674
|
}
|
|
1637
1675
|
}
|
|
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
1676
|
else {
|
|
1680
|
-
//
|
|
1681
|
-
|
|
1682
|
-
|
|
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);
|
|
1677
|
+
// No transcription, clear words and potentially stop sync listeners
|
|
1678
|
+
this.highlightedWords.set([]);
|
|
1679
|
+
this.destroy$.next(); // Signal existing listeners to stop
|
|
1690
1680
|
}
|
|
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
1681
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
//
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
-
});
|
|
1682
|
+
/**
|
|
1683
|
+
* Initialize the audio element and set up event listeners
|
|
1684
|
+
*/
|
|
1685
|
+
initializeAudio(audioUrl) {
|
|
1686
|
+
// Clean up any existing audio element and listeners first
|
|
1687
|
+
this.cleanupAudio();
|
|
1688
|
+
this.audioElement = new Audio(audioUrl); // Use passed URL
|
|
1720
1689
|
}
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1690
|
+
/**
|
|
1691
|
+
* Initialize highlighted words from transcription timestamps
|
|
1692
|
+
*/
|
|
1693
|
+
initializeHighlightedWords(timestamps) {
|
|
1694
|
+
const initialWords = timestamps.map((word, index) => ({
|
|
1695
|
+
word: word.word,
|
|
1696
|
+
index,
|
|
1697
|
+
isHighlighted: false,
|
|
1698
|
+
}));
|
|
1699
|
+
this.highlightedWords.set(initialWords);
|
|
1725
1700
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1701
|
+
/**
|
|
1702
|
+
* Set up audio synchronization with text
|
|
1703
|
+
*/
|
|
1704
|
+
setupAudioSync(timestamps) {
|
|
1705
|
+
// Accept timestamps
|
|
1706
|
+
if (!this.audioElement) {
|
|
1707
|
+
// Guard against missing audio element
|
|
1708
|
+
return;
|
|
1735
1709
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1710
|
+
// Important: Clean up previous listeners before setting up new ones
|
|
1711
|
+
this.destroy$.next(); // Signal previous subscriptions to complete
|
|
1712
|
+
// Listen to timeupdate events
|
|
1713
|
+
fromEvent(this.audioElement, 'timeupdate')
|
|
1714
|
+
.pipe(takeUntilDestroyed(this.destroyRef), // Use DestroyRef for component lifecycle
|
|
1715
|
+
takeUntil(this.destroy$), // Use manual subject for re-initialization cleanup
|
|
1716
|
+
map(() => this.audioElement?.currentTime || 0))
|
|
1717
|
+
.subscribe((currentTime) => {
|
|
1718
|
+
// Use the passed timestamps array
|
|
1719
|
+
const updatedWords = timestamps.map((word, index) => {
|
|
1720
|
+
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1721
|
+
return { word: word.word, index, isHighlighted };
|
|
1722
|
+
});
|
|
1723
|
+
this.highlightedWords.set(updatedWords);
|
|
1724
|
+
});
|
|
1725
|
+
// Listen to ended event for cleanup
|
|
1726
|
+
fromEvent(this.audioElement, 'ended')
|
|
1727
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
|
|
1728
|
+
.subscribe(() => {
|
|
1729
|
+
// Reset highlighting when audio ends
|
|
1730
|
+
const resetWords = this.highlightedWords().map((word) => ({
|
|
1731
|
+
// Read current words signal
|
|
1732
|
+
...word,
|
|
1733
|
+
isHighlighted: false,
|
|
1734
|
+
}));
|
|
1735
|
+
this.highlightedWords.set(resetWords);
|
|
1736
|
+
// Emit audio completed event with the current message
|
|
1737
|
+
const currentMsg = this.message();
|
|
1738
|
+
if (currentMsg) {
|
|
1739
|
+
// Reset the shouldPlayAudio flag
|
|
1740
|
+
currentMsg.shouldPlayAudio = false;
|
|
1741
|
+
this.audioCompleted.emit(currentMsg);
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Clean up audio element and event listeners
|
|
1747
|
+
*/
|
|
1748
|
+
cleanupAudio() {
|
|
1749
|
+
if (this.audioElement) {
|
|
1750
|
+
this.audioElement.pause();
|
|
1751
|
+
this.audioElement.removeAttribute('src'); // Use removeAttribute for better cleanup
|
|
1752
|
+
this.audioElement = null;
|
|
1753
|
+
this.destroy$.next(); // Ensure listeners tied to this audio instance are cleaned up
|
|
1754
|
+
// Reset highlighting immediately on cleanup
|
|
1755
|
+
const resetWords = this.highlightedWords().map((word) => ({
|
|
1756
|
+
...word,
|
|
1757
|
+
isHighlighted: false,
|
|
1758
|
+
}));
|
|
1759
|
+
this.highlightedWords.set(resetWords);
|
|
1739
1760
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Play or pause the audio
|
|
1764
|
+
*/
|
|
1765
|
+
onPlayMessage() {
|
|
1766
|
+
const currentMsg = this.message(); // Read signal
|
|
1767
|
+
if (this.audioElement) {
|
|
1768
|
+
if (this.audioElement.paused) {
|
|
1769
|
+
this.startAudioPlayback();
|
|
1744
1770
|
}
|
|
1745
1771
|
else {
|
|
1746
|
-
|
|
1747
|
-
return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
|
|
1772
|
+
this.audioElement.pause();
|
|
1748
1773
|
}
|
|
1749
1774
|
}
|
|
1775
|
+
else if (currentMsg?.audioUrl) {
|
|
1776
|
+
// Initialization now happens via effect. If element doesn't exist yet,
|
|
1777
|
+
// the effect should create it. Clicking play might need to ensure init runs.
|
|
1778
|
+
// For simplicity, let's re-call initializeAudio here if needed,
|
|
1779
|
+
// though ideally the effect handles it.
|
|
1780
|
+
this.initializeAudio(currentMsg.audioUrl); // Ensure it's created if somehow missed
|
|
1781
|
+
this.startAudioPlayback();
|
|
1782
|
+
}
|
|
1783
|
+
else if (currentMsg) {
|
|
1784
|
+
// If no audio URL is available, emit event to parent component
|
|
1785
|
+
this.playAudio.emit(currentMsg); // Emit the message value
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Start audio playback and handle any setup needed
|
|
1790
|
+
*/
|
|
1791
|
+
startAudioPlayback() {
|
|
1792
|
+
if (!this.audioElement)
|
|
1793
|
+
return;
|
|
1794
|
+
// Play the audio
|
|
1795
|
+
this.audioElement.play().catch((error) => {
|
|
1796
|
+
console.error('Error playing audio:', error);
|
|
1797
|
+
});
|
|
1750
1798
|
}
|
|
1751
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1752
|
-
static { this.ɵ
|
|
1799
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StandaloneAudioTextSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1800
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: StandaloneAudioTextSyncComponent, isStandalone: true, selector: "dc-standalone-audio-text-sync", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <!-- Icon for play/loading status -->\n <div>\n <!-- <span> lo: {{ isLoading() }} tr: {{ hasTranscription() }} play: {{ shouldPlayAudio() }} </span> -->\n </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (isLoading()) {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n\n } @else {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div class=\"text-content\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText()\"></span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:8px}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1753
1801
|
}
|
|
1754
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1755
|
-
type:
|
|
1756
|
-
args: [{
|
|
1757
|
-
providedIn: 'root',
|
|
1758
|
-
}]
|
|
1802
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StandaloneAudioTextSyncComponent, decorators: [{
|
|
1803
|
+
type: Component,
|
|
1804
|
+
args: [{ selector: 'dc-standalone-audio-text-sync', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <!-- Icon for play/loading status -->\n <div>\n <!-- <span> lo: {{ isLoading() }} tr: {{ hasTranscription() }} play: {{ shouldPlayAudio() }} </span> -->\n </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (isLoading()) {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n\n } @else {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div class=\"text-content\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText()\"></span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:8px}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
1759
1805
|
}], ctorParameters: () => [] });
|
|
1760
1806
|
|
|
1761
|
-
class
|
|
1762
|
-
constructor(
|
|
1763
|
-
this.
|
|
1764
|
-
this.
|
|
1765
|
-
this.
|
|
1766
|
-
|
|
1767
|
-
this.
|
|
1768
|
-
this.agentCardSignal = signal(null);
|
|
1769
|
-
this.isDestroyedSignal = signal(false);
|
|
1807
|
+
class MultiMessageContentComponent {
|
|
1808
|
+
constructor() {
|
|
1809
|
+
this.messages = input.required();
|
|
1810
|
+
this.isLoading = input(false);
|
|
1811
|
+
this.playAudio = output();
|
|
1812
|
+
// Track current playing message index
|
|
1813
|
+
this.currentPlayingIndex = -1;
|
|
1770
1814
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1815
|
+
/**
|
|
1816
|
+
* Checks if a message has transcription timestamps
|
|
1817
|
+
* @param message The message to check
|
|
1818
|
+
*/
|
|
1819
|
+
hasTranscription(message) {
|
|
1820
|
+
//
|
|
1821
|
+
return !!message.transcriptionTimestamps;
|
|
1774
1822
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1823
|
+
/**
|
|
1824
|
+
* Emits the playAudio event with the selected message and starts sequential playback
|
|
1825
|
+
* @param message The message to play
|
|
1826
|
+
*/
|
|
1827
|
+
onPlayMessage(message) {
|
|
1828
|
+
// Find the index of the message to play
|
|
1829
|
+
const index = this.messages().findIndex((msg) => msg === message || msg.messageId === message.messageId);
|
|
1830
|
+
if (index >= 0) {
|
|
1831
|
+
this.startSequentialPlayback(index);
|
|
1832
|
+
}
|
|
1833
|
+
// Also emit to parent if needed
|
|
1834
|
+
this.playAudio.emit(message);
|
|
1778
1835
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1836
|
+
/**
|
|
1837
|
+
* Method to start playing the sequence
|
|
1838
|
+
* @param startIndex Index of the message to start playing from
|
|
1839
|
+
*/
|
|
1840
|
+
startSequentialPlayback(startIndex = 0) {
|
|
1841
|
+
this.currentPlayingIndex = startIndex;
|
|
1842
|
+
const messages = this.messages();
|
|
1843
|
+
if (messages && messages.length > startIndex) {
|
|
1844
|
+
// Reset all messages
|
|
1845
|
+
messages.forEach((msg) => (msg.shouldPlayAudio = false));
|
|
1846
|
+
// Set the current message to play
|
|
1847
|
+
messages[startIndex].shouldPlayAudio = true;
|
|
1848
|
+
}
|
|
1782
1849
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1850
|
+
/**
|
|
1851
|
+
* Handle audio completion from child component
|
|
1852
|
+
* @param message The message that completed playback
|
|
1853
|
+
*/
|
|
1854
|
+
onAudioCompleted(message) {
|
|
1855
|
+
// Find the index of the completed message
|
|
1856
|
+
const completedIndex = this.messages().findIndex((msg) => msg === message || msg.messageId === message.messageId);
|
|
1857
|
+
if (completedIndex >= 0 && completedIndex < this.messages().length - 1) {
|
|
1858
|
+
// Reset current message
|
|
1859
|
+
const messages = this.messages();
|
|
1860
|
+
messages[completedIndex].shouldPlayAudio = false;
|
|
1861
|
+
// Play the next message
|
|
1862
|
+
this.currentPlayingIndex = completedIndex + 1;
|
|
1863
|
+
messages[this.currentPlayingIndex].shouldPlayAudio = true;
|
|
1864
|
+
}
|
|
1865
|
+
else {
|
|
1866
|
+
// All messages played or message not found
|
|
1867
|
+
this.currentPlayingIndex = -1;
|
|
1868
|
+
}
|
|
1786
1869
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
this.isDestroyedSignal.set(value);
|
|
1870
|
+
ngOnInit() {
|
|
1871
|
+
console.log(this.messages());
|
|
1790
1872
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1873
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiMessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1874
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: MultiMessageContentComponent, isStandalone: true, selector: "dc-multi-message-content", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "@for (message of messages(); track message) {\n <!-- Message with transcription -->\n @if (hasTranscription(message)) {\n <dc-standalone-audio-text-sync [message]=\"message\" (playAudio)=\"onPlayMessage(message)\" (audioCompleted)=\"onAudioCompleted(message)\"></dc-standalone-audio-text-sync>\n }@else {\n <!-- Message without transcription -->\n\n <div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading(), message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n </div>\n } }\n\n <!-- Icon template for play/loading status -->\n <ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n @if (isLoading) {\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n }\n @if (!isLoading) {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n </ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: StandaloneAudioTextSyncComponent, selector: "dc-standalone-audio-text-sync", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1875
|
+
}
|
|
1876
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiMessageContentComponent, decorators: [{
|
|
1877
|
+
type: Component,
|
|
1878
|
+
args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [IconsComponent, StandaloneAudioTextSyncComponent, CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messages(); track message) {\n <!-- Message with transcription -->\n @if (hasTranscription(message)) {\n <dc-standalone-audio-text-sync [message]=\"message\" (playAudio)=\"onPlayMessage(message)\" (audioCompleted)=\"onAudioCompleted(message)\"></dc-standalone-audio-text-sync>\n }@else {\n <!-- Message without transcription -->\n\n <div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading(), message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n </div>\n } }\n\n <!-- Icon template for play/loading status -->\n <ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n @if (isLoading) {\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n }\n @if (!isLoading) {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n </ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
1879
|
+
}], ctorParameters: () => [] });
|
|
1880
|
+
|
|
1881
|
+
function matchTranscription(originalText, transcription) {
|
|
1882
|
+
const result = [];
|
|
1883
|
+
const transcriptionMap = new Map();
|
|
1884
|
+
// Create a map of lowercase words to an array of their corresponding objects in transcription
|
|
1885
|
+
for (const obj of transcription) {
|
|
1886
|
+
const lowercaseWord = obj.word.trim().toLowerCase();
|
|
1887
|
+
if (transcriptionMap.has(lowercaseWord)) {
|
|
1888
|
+
transcriptionMap.get(lowercaseWord).push(obj);
|
|
1795
1889
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1799
|
-
this.conversationSettingsSignal.set(conversationSettings);
|
|
1800
|
-
// Update agent card with conversation settings
|
|
1801
|
-
const updatedAgentCard = { ...agentCard, conversationSettings };
|
|
1802
|
-
this.agentCardSignal.set(updatedAgentCard);
|
|
1803
|
-
if (!conversationSettings.messages) {
|
|
1804
|
-
throw new Error('conversationSettings.messages is required in proper format to start conversation');
|
|
1890
|
+
else {
|
|
1891
|
+
transcriptionMap.set(lowercaseWord, [obj]);
|
|
1805
1892
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1893
|
+
}
|
|
1894
|
+
// Split the original text into an array of words
|
|
1895
|
+
const words = originalText.split(' ');
|
|
1896
|
+
let currentTime = 0;
|
|
1897
|
+
for (const word of words) {
|
|
1898
|
+
const lowercaseWord = word.toLowerCase();
|
|
1899
|
+
const matchedObjs = transcriptionMap.get(lowercaseWord);
|
|
1900
|
+
if (matchedObjs && matchedObjs.length > 0) {
|
|
1901
|
+
const matchedObj = matchedObjs.shift();
|
|
1902
|
+
result.push({
|
|
1903
|
+
word: word,
|
|
1904
|
+
start: Number(matchedObj.start.toFixed(3)),
|
|
1905
|
+
end: Number(matchedObj.end.toFixed(3)),
|
|
1906
|
+
});
|
|
1907
|
+
currentTime = matchedObj.end;
|
|
1813
1908
|
}
|
|
1814
|
-
else
|
|
1815
|
-
|
|
1816
|
-
|
|
1909
|
+
else {
|
|
1910
|
+
result.push({
|
|
1911
|
+
word: word,
|
|
1912
|
+
start: Number(currentTime.toFixed(3)),
|
|
1913
|
+
end: Number(currentTime.toFixed(3)),
|
|
1914
|
+
});
|
|
1817
1915
|
}
|
|
1818
1916
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1917
|
+
return result;
|
|
1918
|
+
}
|
|
1919
|
+
function extractAudioAndTranscription(message, audio) {
|
|
1920
|
+
message.audioUrl = audio.blobUrl;
|
|
1921
|
+
message.transcription = audio.transcription;
|
|
1922
|
+
if (message.transcription) {
|
|
1923
|
+
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
function buildObjectTTSRequest(message, settings = {}) {
|
|
1927
|
+
const generateTranscription = settings?.highlightWords ?? false;
|
|
1928
|
+
const speedRate = settings?.speedRate || 0;
|
|
1929
|
+
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
|
|
1930
|
+
const text = removeEmojis(message.text || message.content);
|
|
1931
|
+
return {
|
|
1932
|
+
text,
|
|
1933
|
+
voice: message.voice || settings.voice,
|
|
1934
|
+
generateTranscription,
|
|
1935
|
+
speedRate,
|
|
1936
|
+
speed,
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
class ChatMessageComponent {
|
|
1941
|
+
constructor() {
|
|
1942
|
+
this.audioService = inject(AudioService);
|
|
1943
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1944
|
+
this.audioTextSyncService = inject(AudioTextSyncService);
|
|
1945
|
+
this.chatMessage = input.required();
|
|
1946
|
+
this.chatUserSettings = input(null);
|
|
1947
|
+
this.audioMessage = signal(null);
|
|
1948
|
+
this.destroyRef = inject(DestroyRef);
|
|
1949
|
+
this.conversationChatSettings = signal(null);
|
|
1950
|
+
this.isLoading = signal(false);
|
|
1951
|
+
// Field initializer for the effect - runs during component creation
|
|
1952
|
+
this.messageEffect = effect(() => {
|
|
1953
|
+
const message = this.chatMessage();
|
|
1954
|
+
if (!message)
|
|
1955
|
+
return;
|
|
1956
|
+
const settings$ = this.conversationChatSettings();
|
|
1957
|
+
// Skip processing if settings aren't loaded yet
|
|
1958
|
+
if (!settings$)
|
|
1959
|
+
return;
|
|
1960
|
+
if (message.role === ChatRole.AssistantHelper) {
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
if (message.role === ChatRole.User) {
|
|
1964
|
+
if (settings$.repeatRecording && settings$.synthVoice && message.audioUrl) {
|
|
1965
|
+
this.playMessage(message);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
else if (settings$.synthVoice) {
|
|
1969
|
+
if (message.multiMessages) {
|
|
1970
|
+
this.generateAndPlayAllAudios(message.multiMessages);
|
|
1971
|
+
}
|
|
1972
|
+
else {
|
|
1973
|
+
this.generateAndPlayAudio(message);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
// Computed properties for easier access to signal values
|
|
1978
|
+
this.messageRole = computed(() => this.chatMessage()?.role);
|
|
1979
|
+
this.hasMultiMessages = computed(() => !!this.chatMessage()?.multiMessages);
|
|
1980
|
+
this.multiMessages = computed(() => this.chatMessage()?.multiMessages || []);
|
|
1981
|
+
this.messageContent = computed(() => this.chatMessage()?.content || '');
|
|
1982
|
+
this.messageTranslation = computed(() => this.chatMessage()?.translation);
|
|
1983
|
+
this.isUserMessage = computed(() => this.messageRole() === ChatRole.User);
|
|
1984
|
+
this.isAssistantMessage = computed(() => this.messageRole() === ChatRole.Assistant);
|
|
1985
|
+
this.isAssistantHelperMessage = computed(() => this.messageRole() === ChatRole.AssistantHelper);
|
|
1986
|
+
}
|
|
1987
|
+
async ngOnInit() {
|
|
1988
|
+
// Load settings - the effect will react to this change
|
|
1989
|
+
const settings = await this.agentCardService.getConversationUserChatSettings();
|
|
1990
|
+
this.conversationChatSettings.set(settings);
|
|
1991
|
+
// No need for effect here as it's now a field initializer
|
|
1992
|
+
}
|
|
1993
|
+
async generateAndPlayAudio(message, overwriteText = null) {
|
|
1994
|
+
// this.isLoading.set(true);
|
|
1995
|
+
this.audioMessage.set({ ...message, isLoading: true });
|
|
1828
1996
|
try {
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
message.audioHtml.addEventListener('ended', () => {
|
|
1834
|
-
resolve();
|
|
1835
|
-
});
|
|
1836
|
-
}
|
|
1837
|
-
else {
|
|
1838
|
-
resolve();
|
|
1839
|
-
}
|
|
1840
|
-
});
|
|
1997
|
+
const text = overwriteText || message.content;
|
|
1998
|
+
const ttsObject = buildObjectTTSRequest({ ...message, text }, this.conversationChatSettings());
|
|
1999
|
+
if (message.ssml) {
|
|
2000
|
+
ttsObject.ssml = message.ssml;
|
|
1841
2001
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
2002
|
+
const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
|
|
2003
|
+
extractAudioAndTranscription(message, speechAudio);
|
|
2004
|
+
this.audioMessage.set({ ...message, isLoading: false, shouldPlayAudio: true });
|
|
2005
|
+
// this.playMessage(message);
|
|
1844
2006
|
}
|
|
1845
2007
|
finally {
|
|
1846
|
-
this.
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
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');
|
|
2008
|
+
this.isLoading.set(false);
|
|
1858
2009
|
}
|
|
1859
|
-
return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
|
|
1860
2010
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
if (
|
|
1864
|
-
return;
|
|
1865
|
-
}
|
|
1866
|
-
const messages = this.messagesSignal();
|
|
1867
|
-
const conversationSettings = this.conversationSettingsSignal();
|
|
1868
|
-
const agentCard = this.agentCardSignal();
|
|
1869
|
-
if (!conversationSettings || !agentCard) {
|
|
1870
|
-
throw new Error('Conversation not properly initialized');
|
|
1871
|
-
}
|
|
1872
|
-
if (messages.length > 31) {
|
|
1873
|
-
// Safety limit to prevent infinite conversations
|
|
1874
|
-
return;
|
|
1875
|
-
}
|
|
1876
|
-
let conversationMessages = messages;
|
|
1877
|
-
// Add last prompt if available
|
|
1878
|
-
if (conversationSettings.last_prompt) {
|
|
1879
|
-
conversationMessages = [
|
|
1880
|
-
...messages,
|
|
1881
|
-
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1882
|
-
];
|
|
2011
|
+
playMessage(message) {
|
|
2012
|
+
debugger;
|
|
2013
|
+
if (!message.audioUrl) {
|
|
2014
|
+
return null;
|
|
1883
2015
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
//
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
2016
|
+
const audioElement = this.audioService.playAudio(message.audioUrl);
|
|
2017
|
+
message['audioHtml'] = audioElement;
|
|
2018
|
+
// console.log('Playing message with transcription:', {
|
|
2019
|
+
// hasTranscription: !!message.transcriptionTimestamps,
|
|
2020
|
+
// transcriptionLength: message.transcriptionTimestamps?.length,
|
|
2021
|
+
// audioUrl: message.audioUrl,
|
|
2022
|
+
// audioElement,
|
|
2023
|
+
// });
|
|
2024
|
+
if (message.transcriptionTimestamps) {
|
|
2025
|
+
// Generate a unique ID for this message
|
|
2026
|
+
const messageId = this.generateMessageId(message);
|
|
2027
|
+
console.log('Syncing audio with text:', {
|
|
2028
|
+
messageId,
|
|
2029
|
+
transcriptionTimestamps: message.transcriptionTimestamps,
|
|
2030
|
+
wordCount: message.transcriptionTimestamps.length,
|
|
2031
|
+
});
|
|
2032
|
+
// Use the audio-text sync service instead of direct manipulation
|
|
2033
|
+
// Pass the messageId to ensure each message has its own state
|
|
2034
|
+
// this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
|
|
1896
2035
|
}
|
|
1897
|
-
|
|
1898
|
-
const newMessage = this.processAssistantMessage(response);
|
|
1899
|
-
// Add to messages
|
|
1900
|
-
this.addMessage(newMessage);
|
|
1901
|
-
this.isThinkingSignal.set(false);
|
|
2036
|
+
return audioElement;
|
|
1902
2037
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2038
|
+
/**
|
|
2039
|
+
* Generate a unique ID for a message
|
|
2040
|
+
* Uses message content/text and a timestamp to ensure uniqueness
|
|
2041
|
+
*/
|
|
2042
|
+
generateMessageId(message) {
|
|
2043
|
+
const messageContent = message.content || message.text || '';
|
|
2044
|
+
// Use a safe approach to get an ID-like value since 'id' isn't guaranteed on these types
|
|
2045
|
+
const messageId = message._id || message.id || '';
|
|
2046
|
+
const timestamp = new Date().getTime();
|
|
2047
|
+
return `msg_${messageId}_${messageContent.substring(0, 10).replace(/\s+/g, '_')}_${timestamp}`;
|
|
1907
2048
|
}
|
|
1908
|
-
//
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2049
|
+
// This method is no longer needed as we're using the AudioTextSyncService
|
|
2050
|
+
// It's kept here as a reference but can be removed
|
|
2051
|
+
/*
|
|
2052
|
+
private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
|
|
2053
|
+
// This functionality has been moved to AudioTextSyncService
|
|
2054
|
+
}
|
|
2055
|
+
*/
|
|
2056
|
+
generateAndPlayAllAudios(multiMessages) {
|
|
2057
|
+
if (!multiMessages.length)
|
|
2058
|
+
return;
|
|
2059
|
+
let currentIndex = 0;
|
|
2060
|
+
const playAudioSequentially = async () => {
|
|
2061
|
+
if (currentIndex >= multiMessages.length) {
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
const currentMessage = multiMessages[currentIndex];
|
|
2065
|
+
currentMessage.isLoading = true;
|
|
2066
|
+
try {
|
|
2067
|
+
// Process current message audio if needed
|
|
2068
|
+
if (!currentMessage.audioUrl) {
|
|
2069
|
+
if (currentMessage.audioPromise) {
|
|
2070
|
+
await currentMessage.audioPromise;
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
const request = buildObjectTTSRequest(currentMessage, this.conversationChatSettings());
|
|
2074
|
+
currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2075
|
+
const audio = await currentMessage.audioPromise;
|
|
2076
|
+
extractAudioAndTranscription(currentMessage, audio);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
// Play the current message
|
|
2080
|
+
const audioElement = this.playMessage(currentMessage);
|
|
2081
|
+
if (audioElement) {
|
|
2082
|
+
audioElement.addEventListener('ended', () => {
|
|
2083
|
+
currentIndex++;
|
|
2084
|
+
playAudioSequentially();
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
else {
|
|
2088
|
+
// If playback failed, move to next message
|
|
2089
|
+
currentIndex++;
|
|
2090
|
+
playAudioSequentially();
|
|
2091
|
+
}
|
|
2092
|
+
// Preload next message audio
|
|
2093
|
+
this.preloadNextMessageAudio(multiMessages, currentIndex);
|
|
2094
|
+
}
|
|
2095
|
+
finally {
|
|
2096
|
+
currentMessage.isLoading = false;
|
|
2097
|
+
}
|
|
2098
|
+
};
|
|
2099
|
+
// Start the sequential playback
|
|
2100
|
+
playAudioSequentially();
|
|
2101
|
+
}
|
|
2102
|
+
preloadNextMessageAudio(messages, currentIndex) {
|
|
2103
|
+
if (currentIndex + 1 < messages.length) {
|
|
2104
|
+
const nextMessage = messages[currentIndex + 1];
|
|
2105
|
+
if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
|
|
2106
|
+
const request = buildObjectTTSRequest(nextMessage, this.conversationChatSettings());
|
|
2107
|
+
nextMessage.isLoading = true;
|
|
2108
|
+
nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2109
|
+
nextMessage.audioPromise.then((audio) => {
|
|
2110
|
+
extractAudioAndTranscription(nextMessage, audio);
|
|
2111
|
+
nextMessage.isLoading = false;
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
1920
2114
|
}
|
|
1921
2115
|
}
|
|
1922
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1923
|
-
static { this.ɵ
|
|
2116
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2117
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: { classPropertyName: "chatMessage", publicName: "chatMessage", isSignal: true, isRequired: true, transformFunction: null }, chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <span class=\"avatar-initial\">A</span>\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-multi-message-content [messages]=\"multiMessages()\" (playAudio)=\"playMessage($event)\"></dc-multi-message-content>\n } @else {\n <dc-standalone-audio-text-sync [message]=\"audioMessage()\"></dc-standalone-audio-text-sync>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <span class=\"avatar-initial\">U</span>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row-reverse}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-right:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MultiMessageContentComponent, selector: "dc-multi-message-content", inputs: ["messages", "isLoading"], outputs: ["playAudio"] }, { kind: "component", type: StandaloneAudioTextSyncComponent, selector: "dc-standalone-audio-text-sync", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1924
2118
|
}
|
|
1925
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1926
|
-
type:
|
|
1927
|
-
args: [{
|
|
1928
|
-
|
|
1929
|
-
}]
|
|
1930
|
-
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
1931
|
-
type: Inject,
|
|
1932
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
1933
|
-
}] }, { type: MessageProcessingService }] });
|
|
2119
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
2120
|
+
type: Component,
|
|
2121
|
+
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MultiMessageContentComponent, StandaloneAudioTextSyncComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <span class=\"avatar-initial\">A</span>\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-multi-message-content [messages]=\"multiMessages()\" (playAudio)=\"playMessage($event)\"></dc-multi-message-content>\n } @else {\n <dc-standalone-audio-text-sync [message]=\"audioMessage()\"></dc-standalone-audio-text-sync>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <span class=\"avatar-initial\">U</span>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row-reverse}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-right:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"] }]
|
|
2122
|
+
}], ctorParameters: () => [] });
|
|
1934
2123
|
|
|
1935
2124
|
class ChatMessagesListComponent {
|
|
1936
2125
|
constructor() {
|
|
2126
|
+
this.chatUserSettings = input.required();
|
|
2127
|
+
this.thinkingImg = input.required();
|
|
1937
2128
|
this.aiIcon = 'assets/default/ai.png';
|
|
1938
2129
|
this.conversationService = inject(ConversationService);
|
|
2130
|
+
// Create a signal for the filter text
|
|
2131
|
+
this.filterText = signal('');
|
|
1939
2132
|
// Get messages and thinking state from the conversation service
|
|
1940
|
-
this.messages =
|
|
2133
|
+
this.messages = computed(() => {
|
|
2134
|
+
// Get the actual array of messages from the signal by calling it as a function
|
|
2135
|
+
const allMessages = this.conversationService.getMessages()();
|
|
2136
|
+
return allMessages.filter((message) => message.role !== 'system');
|
|
2137
|
+
});
|
|
1941
2138
|
this.isThinking = this.conversationService.isThinking();
|
|
1942
2139
|
}
|
|
2140
|
+
// Method to update the filter text
|
|
2141
|
+
updateFilter(value) {
|
|
2142
|
+
this.filterText.set(value);
|
|
2143
|
+
}
|
|
1943
2144
|
// Track messages by their content and role for efficient rendering
|
|
1944
2145
|
trackByMessage(index, message) {
|
|
1945
2146
|
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
1946
2147
|
}
|
|
1947
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1948
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2148
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2149
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null }, thinkingImg: { classPropertyName: "thinkingImg", publicName: "thinkingImg", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1949
2150
|
}
|
|
1950
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2151
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
1951
2152
|
type: Component,
|
|
1952
|
-
args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [
|
|
1953
|
-
}]
|
|
1954
|
-
type: Input
|
|
1955
|
-
}], thinkingImg: [{
|
|
1956
|
-
type: Input
|
|
1957
|
-
}] } });
|
|
2153
|
+
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"] }]
|
|
2154
|
+
}] });
|
|
1958
2155
|
|
|
1959
2156
|
const SpeedDescription = {
|
|
1960
2157
|
1: 'Muy Lento',
|
|
@@ -1967,10 +2164,10 @@ class SpeedDescPipe {
|
|
|
1967
2164
|
transform(speed) {
|
|
1968
2165
|
return SpeedDescription[speed];
|
|
1969
2166
|
}
|
|
1970
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1971
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2167
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2168
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, isStandalone: true, name: "speedDisplay" }); }
|
|
1972
2169
|
}
|
|
1973
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, decorators: [{
|
|
1974
2171
|
type: Pipe,
|
|
1975
2172
|
args: [{
|
|
1976
2173
|
name: 'speedDisplay',
|
|
@@ -1987,10 +2184,10 @@ class TruncatePipe {
|
|
|
1987
2184
|
}
|
|
1988
2185
|
return value.substring(0, length) + suffix;
|
|
1989
2186
|
}
|
|
1990
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1991
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2187
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2188
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, isStandalone: true, name: "truncate" }); }
|
|
1992
2189
|
}
|
|
1993
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2190
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, decorators: [{
|
|
1994
2191
|
type: Pipe,
|
|
1995
2192
|
args: [{
|
|
1996
2193
|
name: 'truncate',
|
|
@@ -2012,9 +2209,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2012
2209
|
* ```
|
|
2013
2210
|
*/
|
|
2014
2211
|
class ProviderSelectorComponent {
|
|
2015
|
-
constructor(
|
|
2016
|
-
this.conversationCardAIService =
|
|
2017
|
-
this.cdr =
|
|
2212
|
+
constructor() {
|
|
2213
|
+
this.conversationCardAIService = inject(CONVERSATION_AI_TOKEN);
|
|
2214
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2018
2215
|
/** Flag indicating whether the models are currently being loaded */
|
|
2019
2216
|
this.isLoadingModels = true;
|
|
2020
2217
|
/** Array of available models for the selected provider */
|
|
@@ -2045,26 +2242,23 @@ class ProviderSelectorComponent {
|
|
|
2045
2242
|
});
|
|
2046
2243
|
}
|
|
2047
2244
|
}
|
|
2048
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2049
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2245
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ProviderSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2246
|
+
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
2247
|
}
|
|
2051
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2248
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ProviderSelectorComponent, decorators: [{
|
|
2052
2249
|
type: Component,
|
|
2053
|
-
args: [{ selector: 'dc-provider-selector', standalone: true, imports: [
|
|
2054
|
-
}], ctorParameters: () => [
|
|
2055
|
-
type: Inject,
|
|
2056
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
2057
|
-
}] }, { type: i0.ChangeDetectorRef }], propDecorators: { parentForm: [{
|
|
2250
|
+
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"] }]
|
|
2251
|
+
}], ctorParameters: () => [], propDecorators: { parentForm: [{
|
|
2058
2252
|
type: Input
|
|
2059
2253
|
}] } });
|
|
2060
2254
|
|
|
2061
2255
|
class DCConversationUserChatSettingsComponent {
|
|
2062
|
-
constructor(
|
|
2063
|
-
this.dialogRef =
|
|
2064
|
-
this.fb =
|
|
2065
|
-
this.conversationCardAIService =
|
|
2256
|
+
constructor() {
|
|
2257
|
+
this.dialogRef = inject(DynamicDialogRef);
|
|
2258
|
+
this.fb = inject(FormBuilder);
|
|
2259
|
+
this.conversationCardAIService = inject(CONVERSATION_AI_TOKEN);
|
|
2066
2260
|
this.isLoadingModels = true;
|
|
2067
|
-
this.showFeature = {
|
|
2261
|
+
this.showFeature = input({
|
|
2068
2262
|
synthVoice: true,
|
|
2069
2263
|
highlightWords: true,
|
|
2070
2264
|
speed: true,
|
|
@@ -2072,8 +2266,8 @@ class DCConversationUserChatSettingsComponent {
|
|
|
2072
2266
|
superHearing: true,
|
|
2073
2267
|
fixGrammar: true,
|
|
2074
2268
|
autoTranslate: true,
|
|
2075
|
-
};
|
|
2076
|
-
this.onSettingsChange =
|
|
2269
|
+
});
|
|
2270
|
+
this.onSettingsChange = output();
|
|
2077
2271
|
this.textEngines = Object.values(TextEngines);
|
|
2078
2272
|
this.voiceTTSOptions = Object.values(VoiceTTSOptions);
|
|
2079
2273
|
this.textEngineOptions = TextEngineOptions;
|
|
@@ -2118,13 +2312,12 @@ class DCConversationUserChatSettingsComponent {
|
|
|
2118
2312
|
this.dialogRef.close(this.form.value);
|
|
2119
2313
|
}
|
|
2120
2314
|
}
|
|
2121
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2122
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2315
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2316
|
+
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
2317
|
}
|
|
2124
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
|
|
2125
2319
|
type: Component,
|
|
2126
2320
|
args: [{ selector: 'dc-chat-settings-dialog', standalone: true, imports: [
|
|
2127
|
-
CommonModule,
|
|
2128
2321
|
ReactiveFormsModule,
|
|
2129
2322
|
CheckboxModule,
|
|
2130
2323
|
SliderModule,
|
|
@@ -2136,16 +2329,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2136
2329
|
BadgeModule,
|
|
2137
2330
|
SkeletonModule,
|
|
2138
2331
|
TooltipModule,
|
|
2139
|
-
ProviderSelectorComponent
|
|
2140
|
-
], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\"
|
|
2141
|
-
}], ctorParameters: () => [
|
|
2142
|
-
type: Inject,
|
|
2143
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
2144
|
-
}] }], propDecorators: { showFeature: [{
|
|
2145
|
-
type: Input
|
|
2146
|
-
}], onSettingsChange: [{
|
|
2147
|
-
type: Output
|
|
2148
|
-
}], tooltipRef: [{
|
|
2332
|
+
ProviderSelectorComponent
|
|
2333
|
+
], 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"] }]
|
|
2334
|
+
}], ctorParameters: () => [], propDecorators: { tooltipRef: [{
|
|
2149
2335
|
type: ViewChild,
|
|
2150
2336
|
args: ['tooltipRef']
|
|
2151
2337
|
}] } });
|
|
@@ -2163,104 +2349,16 @@ const DefaultEvaluatorAgentCard = {
|
|
|
2163
2349
|
sources: [],
|
|
2164
2350
|
};
|
|
2165
2351
|
|
|
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
2352
|
class DCChatComponent {
|
|
2254
|
-
constructor(
|
|
2255
|
-
this.agentCardService =
|
|
2256
|
-
this.
|
|
2257
|
-
this.
|
|
2258
|
-
this.
|
|
2259
|
-
this.
|
|
2260
|
-
this.
|
|
2261
|
-
this.
|
|
2262
|
-
this.
|
|
2263
|
-
this.sendMessage = new EventEmitter();
|
|
2353
|
+
constructor() {
|
|
2354
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
2355
|
+
this.conversationBuilder = inject(DCConversationPromptBuilderService);
|
|
2356
|
+
this.dialogService = inject(DialogService);
|
|
2357
|
+
this.conversationService = inject(ConversationService);
|
|
2358
|
+
this.evaluationService = inject(EvaluationService);
|
|
2359
|
+
this.evaluatorAgentCard = input(DefaultEvaluatorAgentCard);
|
|
2360
|
+
this.parseDict = input({});
|
|
2361
|
+
this.sendMessage = output();
|
|
2264
2362
|
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
2265
2363
|
this.imageUser = `assets/default/user.svg`;
|
|
2266
2364
|
this.thinkingImg = `assets/default/thinking.svg`;
|
|
@@ -2276,29 +2374,18 @@ class DCChatComponent {
|
|
|
2276
2374
|
this.score = this.evaluationService.getScore();
|
|
2277
2375
|
}
|
|
2278
2376
|
async ngOnInit() {
|
|
2279
|
-
debugger;
|
|
2280
2377
|
// Get user settings if not provided
|
|
2281
2378
|
if (!this.chatUserSettings) {
|
|
2282
2379
|
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2283
2380
|
}
|
|
2284
2381
|
// Initialize conversation
|
|
2285
|
-
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict);
|
|
2382
|
+
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict());
|
|
2286
2383
|
}
|
|
2287
2384
|
ngOnDestroy() {
|
|
2288
2385
|
// Mark conversation as destroyed to prevent async operations
|
|
2289
2386
|
this.conversationService.setDestroyed(true);
|
|
2290
2387
|
}
|
|
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
|
-
}
|
|
2388
|
+
// onUserMessage method has been moved to the chat-footer component
|
|
2302
2389
|
/**
|
|
2303
2390
|
* Handle microphone input finished
|
|
2304
2391
|
*/
|
|
@@ -2328,9 +2415,8 @@ class DCChatComponent {
|
|
|
2328
2415
|
};
|
|
2329
2416
|
message['audioUrl'] = URL.createObjectURL(eventBlob);
|
|
2330
2417
|
// Send message to conversation
|
|
2418
|
+
// The evaluation will happen automatically in the conversation service
|
|
2331
2419
|
await this.conversationService.sendUserMessage(message);
|
|
2332
|
-
// Evaluate conversation
|
|
2333
|
-
this.evaluateConversation();
|
|
2334
2420
|
}
|
|
2335
2421
|
finally {
|
|
2336
2422
|
this.isGettingTranscription = false;
|
|
@@ -2367,35 +2453,16 @@ class DCChatComponent {
|
|
|
2367
2453
|
}
|
|
2368
2454
|
await this.ngOnInit();
|
|
2369
2455
|
}
|
|
2370
|
-
|
|
2371
|
-
|
|
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 }); }
|
|
2456
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2457
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer \n [isAIThinking]=\"isThinking()\" \n [micSettings]=\"micSettings\" \n [evaluatorAgentCard]=\"evaluatorAgentCard()\" \n (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <!-- @if (score() > 0) {\n <div class=\"score-container\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n } -->\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "evaluatorAgentCard", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "thinkingImg"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2$4.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2379
2458
|
}
|
|
2380
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2459
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
2381
2460
|
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\"
|
|
2383
|
-
}], ctorParameters: () => [
|
|
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: [{
|
|
2461
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer \n [isAIThinking]=\"isThinking()\" \n [micSettings]=\"micSettings\" \n [evaluatorAgentCard]=\"evaluatorAgentCard()\" \n (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <!-- @if (score() > 0) {\n <div class=\"score-container\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n } -->\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
|
|
2462
|
+
}], ctorParameters: () => [], propDecorators: { chatUserSettings: [{
|
|
2390
2463
|
type: Input
|
|
2391
2464
|
}], agentCard: [{
|
|
2392
2465
|
type: Input
|
|
2393
|
-
}], evaluatorAgentCard: [{
|
|
2394
|
-
type: Input
|
|
2395
|
-
}], parseDict: [{
|
|
2396
|
-
type: Input
|
|
2397
|
-
}], sendMessage: [{
|
|
2398
|
-
type: Output
|
|
2399
2466
|
}] } });
|
|
2400
2467
|
|
|
2401
2468
|
async function getCharacterData(file) {
|
|
@@ -2529,32 +2596,32 @@ function getFileBuffer(file) {
|
|
|
2529
2596
|
}
|
|
2530
2597
|
|
|
2531
2598
|
class PromptPreviewDialogComponent {
|
|
2532
|
-
constructor(
|
|
2533
|
-
this.dynamicDialogConfig =
|
|
2534
|
-
this.dialogRef =
|
|
2599
|
+
constructor() {
|
|
2600
|
+
this.dynamicDialogConfig = inject(DynamicDialogConfig);
|
|
2601
|
+
this.dialogRef = inject(DynamicDialogRef);
|
|
2535
2602
|
this.data = this.dynamicDialogConfig.data;
|
|
2536
2603
|
}
|
|
2537
2604
|
close() {
|
|
2538
2605
|
this.dialogRef.close();
|
|
2539
2606
|
}
|
|
2540
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2541
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
2607
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PromptPreviewDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2608
|
+
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
2609
|
}
|
|
2543
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2610
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PromptPreviewDialogComponent, decorators: [{
|
|
2544
2611
|
type: Component,
|
|
2545
2612
|
args: [{
|
|
2546
2613
|
selector: 'dc-prompt-preview-dialog',
|
|
2547
2614
|
standalone: true,
|
|
2548
|
-
imports: [
|
|
2615
|
+
imports: [DialogModule],
|
|
2549
2616
|
template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `,
|
|
2550
2617
|
}]
|
|
2551
|
-
}], ctorParameters: () => [
|
|
2618
|
+
}], ctorParameters: () => [] });
|
|
2552
2619
|
|
|
2553
2620
|
class TranslateDialogComponent {
|
|
2554
|
-
constructor(
|
|
2555
|
-
this.fb =
|
|
2556
|
-
this.dialogRef =
|
|
2557
|
-
this.data =
|
|
2621
|
+
constructor() {
|
|
2622
|
+
this.fb = inject(FormBuilder);
|
|
2623
|
+
this.dialogRef = inject(DialogRef);
|
|
2624
|
+
this.data = inject(DIALOG_DATA);
|
|
2558
2625
|
this.languages = Object.entries(LangCodeDescriptionEs).map(([code, description]) => ({
|
|
2559
2626
|
code,
|
|
2560
2627
|
description,
|
|
@@ -2570,17 +2637,19 @@ class TranslateDialogComponent {
|
|
|
2570
2637
|
onConfirm() {
|
|
2571
2638
|
this.dialogRef.close(this.form.value.targetLang);
|
|
2572
2639
|
}
|
|
2573
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2574
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
2640
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslateDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2641
|
+
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
2642
|
<div class="translate-dialog">
|
|
2576
2643
|
<h4>Tu idioma actual es: {{ data.currentLang }}</h4>
|
|
2577
2644
|
<h2>Selecciona el idioma al que quieres traducir</h2>
|
|
2578
2645
|
<form [formGroup]="form">
|
|
2579
2646
|
<select formControlName="targetLang">
|
|
2580
2647
|
<option value="">Select language...</option>
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2648
|
+
@for (lang of languages; track lang) {
|
|
2649
|
+
<option [value]="lang.code">
|
|
2650
|
+
{{ lang.description }}
|
|
2651
|
+
</option>
|
|
2652
|
+
}
|
|
2584
2653
|
</select>
|
|
2585
2654
|
</form>
|
|
2586
2655
|
<div class="actions">
|
|
@@ -2588,20 +2657,22 @@ class TranslateDialogComponent {
|
|
|
2588
2657
|
<button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
|
|
2589
2658
|
</div>
|
|
2590
2659
|
</div>
|
|
2591
|
-
|
|
2660
|
+
`, 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
2661
|
}
|
|
2593
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2662
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslateDialogComponent, decorators: [{
|
|
2594
2663
|
type: Component,
|
|
2595
|
-
args: [{ selector: 'dc-translate-dialog', standalone: true, imports: [
|
|
2664
|
+
args: [{ selector: 'dc-translate-dialog', standalone: true, imports: [ReactiveFormsModule], template: `
|
|
2596
2665
|
<div class="translate-dialog">
|
|
2597
2666
|
<h4>Tu idioma actual es: {{ data.currentLang }}</h4>
|
|
2598
2667
|
<h2>Selecciona el idioma al que quieres traducir</h2>
|
|
2599
2668
|
<form [formGroup]="form">
|
|
2600
2669
|
<select formControlName="targetLang">
|
|
2601
2670
|
<option value="">Select language...</option>
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2671
|
+
@for (lang of languages; track lang) {
|
|
2672
|
+
<option [value]="lang.code">
|
|
2673
|
+
{{ lang.description }}
|
|
2674
|
+
</option>
|
|
2675
|
+
}
|
|
2605
2676
|
</select>
|
|
2606
2677
|
</form>
|
|
2607
2678
|
<div class="actions">
|
|
@@ -2609,11 +2680,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2609
2680
|
<button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
|
|
2610
2681
|
</div>
|
|
2611
2682
|
</div>
|
|
2612
|
-
|
|
2613
|
-
}], ctorParameters: () => [
|
|
2614
|
-
type: Inject,
|
|
2615
|
-
args: [DIALOG_DATA]
|
|
2616
|
-
}] }] });
|
|
2683
|
+
`, 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"] }]
|
|
2684
|
+
}], ctorParameters: () => [] });
|
|
2617
2685
|
|
|
2618
2686
|
function markdownToHTML(markdownText) {
|
|
2619
2687
|
// Convert italics-bold (***text***)
|
|
@@ -2630,10 +2698,10 @@ class SimpleMdToHtmlPipe {
|
|
|
2630
2698
|
transform(text) {
|
|
2631
2699
|
return markdownToHTML(text);
|
|
2632
2700
|
}
|
|
2633
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2634
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2701
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2702
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, isStandalone: true, name: "simpleMdToHtml" }); }
|
|
2635
2703
|
}
|
|
2636
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2704
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, decorators: [{
|
|
2637
2705
|
type: Pipe,
|
|
2638
2706
|
args: [{
|
|
2639
2707
|
name: 'simpleMdToHtml',
|
|
@@ -2647,10 +2715,10 @@ class StartDivToHtmlPipe {
|
|
|
2647
2715
|
text = text.replace(/\n/g, '<br>');
|
|
2648
2716
|
return text;
|
|
2649
2717
|
}
|
|
2650
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2651
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2718
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2719
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, isStandalone: true, name: "startDividerToHtml" }); }
|
|
2652
2720
|
}
|
|
2653
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2721
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, decorators: [{
|
|
2654
2722
|
type: Pipe,
|
|
2655
2723
|
args: [{
|
|
2656
2724
|
name: 'startDividerToHtml',
|
|
@@ -2663,10 +2731,10 @@ class MdToHtmlArrayPipe {
|
|
|
2663
2731
|
const htmlArray = convertToHTML(text);
|
|
2664
2732
|
return htmlArray.map((val) => val.content).join('');
|
|
2665
2733
|
}
|
|
2666
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2667
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2734
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2735
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, isStandalone: true, name: "mdToHtmlArray" }); }
|
|
2668
2736
|
}
|
|
2669
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2737
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, decorators: [{
|
|
2670
2738
|
type: Pipe,
|
|
2671
2739
|
args: [{
|
|
2672
2740
|
name: 'mdToHtmlArray',
|
|
@@ -2675,11 +2743,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2675
2743
|
}] });
|
|
2676
2744
|
|
|
2677
2745
|
class AccountPlatformForm {
|
|
2678
|
-
constructor(
|
|
2679
|
-
this.route =
|
|
2680
|
-
this.fb =
|
|
2681
|
-
this.router =
|
|
2682
|
-
this.toastService =
|
|
2746
|
+
constructor() {
|
|
2747
|
+
this.route = inject(ActivatedRoute);
|
|
2748
|
+
this.fb = inject(FormBuilder);
|
|
2749
|
+
this.router = inject(Router);
|
|
2750
|
+
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
2683
2751
|
// Format the platform options for dropdown display
|
|
2684
2752
|
this.platformOptions = Object.values(EAccountsPlatform).map((value) => ({
|
|
2685
2753
|
label: value.charAt(0).toUpperCase() + value.slice(1), // Capitalize first letter
|
|
@@ -2734,16 +2802,13 @@ class AccountPlatformForm {
|
|
|
2734
2802
|
this.toastService.warn(infoToast);
|
|
2735
2803
|
}
|
|
2736
2804
|
}
|
|
2737
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2738
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2805
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2806
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i3$2.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2739
2807
|
}
|
|
2740
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2808
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, decorators: [{
|
|
2741
2809
|
type: Component,
|
|
2742
2810
|
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: () => [
|
|
2744
|
-
type: Inject,
|
|
2745
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
2746
|
-
}] }], propDecorators: { formArray: [{
|
|
2811
|
+
}], ctorParameters: () => [], propDecorators: { formArray: [{
|
|
2747
2812
|
type: Input
|
|
2748
2813
|
}] } });
|
|
2749
2814
|
|
|
@@ -2756,16 +2821,16 @@ class DCAgentCardFormComponent {
|
|
|
2756
2821
|
};
|
|
2757
2822
|
return imageSettings;
|
|
2758
2823
|
}
|
|
2759
|
-
constructor(
|
|
2760
|
-
this.fb =
|
|
2761
|
-
this.storageService =
|
|
2762
|
-
this.agentCardService =
|
|
2763
|
-
this.cdr =
|
|
2764
|
-
this.router =
|
|
2765
|
-
this.activatedRoute =
|
|
2766
|
-
this.dialogService =
|
|
2767
|
-
this.promptBuilder =
|
|
2768
|
-
this.toastService =
|
|
2824
|
+
constructor() {
|
|
2825
|
+
this.fb = inject(FormBuilder);
|
|
2826
|
+
this.storageService = inject(MultiImagesStorageService);
|
|
2827
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
2828
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2829
|
+
this.router = inject(Router);
|
|
2830
|
+
this.activatedRoute = inject(ActivatedRoute);
|
|
2831
|
+
this.dialogService = inject(DialogService);
|
|
2832
|
+
this.promptBuilder = inject(DCConversationPromptBuilderService);
|
|
2833
|
+
this.toastService = inject(TOAST_ALERTS_TOKEN, { optional: true });
|
|
2769
2834
|
// select options
|
|
2770
2835
|
this.textEngines = Object.values(TextEngines);
|
|
2771
2836
|
this.conversationOptions = ConversationTypeOptions;
|
|
@@ -2775,26 +2840,26 @@ class DCAgentCardFormComponent {
|
|
|
2775
2840
|
this.languageOptions = Object.entries(LangCodeDescriptionEs).map(([value, label]) => ({ value, label }));
|
|
2776
2841
|
this.agentCardId = this.activatedRoute.snapshot.paramMap.get('id');
|
|
2777
2842
|
this.audioSpeedOptions = Object.values(AudioSpeed$1).map((speed) => ({ label: speed, value: speed }));
|
|
2778
|
-
this.storageSettings = this.getSettings();
|
|
2779
|
-
this.bannerImgSettings = {
|
|
2843
|
+
this.storageSettings = input(this.getSettings());
|
|
2844
|
+
this.bannerImgSettings = input({
|
|
2780
2845
|
path: 'conversation-cards/' + this.agentCardId,
|
|
2781
2846
|
fileName: null,
|
|
2782
2847
|
cropSettings: { aspectRatio: AspectType.Rectangle, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 700 },
|
|
2783
|
-
};
|
|
2784
|
-
this.imageStorageSettings = {
|
|
2848
|
+
});
|
|
2849
|
+
this.imageStorageSettings = input({
|
|
2785
2850
|
path: 'conversation-cards/' + this.agentCardId,
|
|
2786
2851
|
fileName: null,
|
|
2787
2852
|
cropSettings: { aspectRatio: AspectType.Vertical_9_16, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 500 },
|
|
2788
|
-
};
|
|
2853
|
+
});
|
|
2789
2854
|
this.stickerStorageSettings = {
|
|
2790
2855
|
path: `conversation-cards/${this.agentCardId}/stickers`,
|
|
2791
2856
|
fileName: null,
|
|
2792
2857
|
cropSettings: { aspectRatio: AspectType.Square, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 400 },
|
|
2793
2858
|
};
|
|
2794
|
-
this.onImageLoaded =
|
|
2795
|
-
this.onSave =
|
|
2796
|
-
this.onGoDetails =
|
|
2797
|
-
this.onTranslate =
|
|
2859
|
+
this.onImageLoaded = output();
|
|
2860
|
+
this.onSave = output();
|
|
2861
|
+
this.onGoDetails = output();
|
|
2862
|
+
this.onTranslate = output();
|
|
2798
2863
|
this.markdownForm = this.fb.group({ seeMarkdown: [false] });
|
|
2799
2864
|
// Type is IAgentCard
|
|
2800
2865
|
this.form = this.fb.group({
|
|
@@ -2831,7 +2896,7 @@ class DCAgentCardFormComponent {
|
|
|
2831
2896
|
ngOnInit() {
|
|
2832
2897
|
this.form.controls.conversationSettings.controls.autoStart;
|
|
2833
2898
|
console.log('form', this.form.value, this.conversationOptions);
|
|
2834
|
-
this.imageSettings = this.storageSettings.cropSettings;
|
|
2899
|
+
this.imageSettings = this.storageSettings().cropSettings;
|
|
2835
2900
|
this.loadConversationCard();
|
|
2836
2901
|
this.markdownForm.get('checked')?.valueChanges.subscribe((value) => {
|
|
2837
2902
|
this.markdownForm.patchValue({ seeMarkdown: value }, { emitEvent: false });
|
|
@@ -2846,7 +2911,7 @@ class DCAgentCardFormComponent {
|
|
|
2846
2911
|
this.patchFormWithConversationData();
|
|
2847
2912
|
this.handleAlternateGreetings();
|
|
2848
2913
|
}
|
|
2849
|
-
this.storageSettings.path = 'conversation-cards/' + this.agentCardId;
|
|
2914
|
+
this.storageSettings().path = 'conversation-cards/' + this.agentCardId;
|
|
2850
2915
|
this.cdr.detectChanges();
|
|
2851
2916
|
}
|
|
2852
2917
|
catch (err) {
|
|
@@ -3164,13 +3229,12 @@ class DCAgentCardFormComponent {
|
|
|
3164
3229
|
this.toastService.success({ title: 'Sticker removed', subtitle: 'Sticker was removed' });
|
|
3165
3230
|
this.cdr.detectChanges();
|
|
3166
3231
|
}
|
|
3167
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
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)\">✖</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"] }] }); }
|
|
3232
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3233
|
+
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)\">✖</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
3234
|
}
|
|
3170
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3235
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
|
|
3171
3236
|
type: Component,
|
|
3172
3237
|
args: [{ selector: 'dc-agent-form', standalone: true, providers: [DialogService], imports: [
|
|
3173
|
-
CommonModule,
|
|
3174
3238
|
ReactiveFormsModule,
|
|
3175
3239
|
CropperComponentModal,
|
|
3176
3240
|
OverlayModule,
|
|
@@ -3189,31 +3253,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3189
3253
|
DynamicDialogModule,
|
|
3190
3254
|
PopoverModule,
|
|
3191
3255
|
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)\">✖</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: () => [
|
|
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
|
-
}] } });
|
|
3256
|
+
AccountPlatformForm
|
|
3257
|
+
], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">✖</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
|
|
3258
|
+
}], ctorParameters: () => [] });
|
|
3217
3259
|
|
|
3218
3260
|
class DCConversationCardUIComponent {
|
|
3219
3261
|
constructor() {
|
|
@@ -3222,36 +3264,31 @@ class DCConversationCardUIComponent {
|
|
|
3222
3264
|
{ label: 'Delete', icon: 'pi pi-trash', title: 'delete', severity: 'danger', command: () => this.onDelete() },
|
|
3223
3265
|
{ label: 'Select', icon: 'pi pi-check', title: 'select', severity: 'success', command: () => this.onDetails() },
|
|
3224
3266
|
];
|
|
3225
|
-
this.
|
|
3226
|
-
this.
|
|
3267
|
+
this.card = input(undefined);
|
|
3268
|
+
this.showOptions = input(true);
|
|
3269
|
+
this.onCardAction = output();
|
|
3227
3270
|
}
|
|
3228
3271
|
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);
|
|
3272
|
+
const name = this.card().characterCard.data.name;
|
|
3273
|
+
const description = this.card().characterCard.data.description;
|
|
3274
|
+
this.card().characterCard.data.description = description.replace(/{{char}}/g, name);
|
|
3232
3275
|
}
|
|
3233
3276
|
onDetails() {
|
|
3234
|
-
this.onCardAction.emit({ event: 'details', card: this.card });
|
|
3277
|
+
this.onCardAction.emit({ event: 'details', card: this.card() });
|
|
3235
3278
|
}
|
|
3236
3279
|
onEdit() {
|
|
3237
|
-
this.onCardAction.emit({ event: 'edit', card: this.card });
|
|
3280
|
+
this.onCardAction.emit({ event: 'edit', card: this.card() });
|
|
3238
3281
|
}
|
|
3239
3282
|
onDelete() {
|
|
3240
|
-
this.onCardAction.emit({ event: 'delete', card: this.card });
|
|
3283
|
+
this.onCardAction.emit({ event: 'delete', card: this.card() });
|
|
3241
3284
|
}
|
|
3242
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3243
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
3285
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3286
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onCardAction: "onCardAction" }, ngImport: i0, template: "<p-card class=\"card-image\">\n @if(showOptions()) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card()?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card().title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: PopoverModule }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$6.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3244
3287
|
}
|
|
3245
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3288
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
|
|
3246
3289
|
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
|
-
}]
|
|
3249
|
-
type: Input
|
|
3250
|
-
}], showOptions: [{
|
|
3251
|
-
type: Input
|
|
3252
|
-
}], onCardAction: [{
|
|
3253
|
-
type: Output
|
|
3254
|
-
}] } });
|
|
3290
|
+
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"] }]
|
|
3291
|
+
}] });
|
|
3255
3292
|
|
|
3256
3293
|
// 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
3294
|
// 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 +3299,18 @@ const DefaultColumns = [
|
|
|
3262
3299
|
{ field: 'title', header: 'Title', type: 'text' },
|
|
3263
3300
|
];
|
|
3264
3301
|
class AgentCardListComponent extends PaginationBase {
|
|
3265
|
-
constructor(
|
|
3302
|
+
constructor() {
|
|
3303
|
+
const route = inject(ActivatedRoute);
|
|
3304
|
+
const router = inject(Router);
|
|
3266
3305
|
super(route, router);
|
|
3267
|
-
this.agentCardService =
|
|
3268
|
-
this.toastService =
|
|
3269
|
-
this.cdr =
|
|
3306
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
3307
|
+
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
3308
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
3270
3309
|
this.viewMode = 'cards';
|
|
3271
|
-
this.
|
|
3272
|
-
this.
|
|
3310
|
+
this.customCardComponent = input(undefined);
|
|
3311
|
+
this.showOptions = input(true);
|
|
3312
|
+
this.gridLayout = input(true);
|
|
3313
|
+
this.getCustomButtons = input();
|
|
3273
3314
|
this.agentCards = [];
|
|
3274
3315
|
this.cardEventSubs = [];
|
|
3275
3316
|
this.cardComponent = null;
|
|
@@ -3279,7 +3320,7 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3279
3320
|
// this.buttonActions = DefaultActions;
|
|
3280
3321
|
this.filterConfig.returnProps = { _id: 1, title: 1, assets: 1, description: 1, 'characterCard.data.description': 1, 'characterCard.data.name': 1 };
|
|
3281
3322
|
this.loadConversationCards();
|
|
3282
|
-
this.cardComponent = this.customCardComponent || DCConversationCardUIComponent;
|
|
3323
|
+
this.cardComponent = this.customCardComponent() || DCConversationCardUIComponent;
|
|
3283
3324
|
}
|
|
3284
3325
|
subscribeDinamicInstantToEvents() {
|
|
3285
3326
|
// Clear previous cardEventSubs
|
|
@@ -3289,9 +3330,12 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3289
3330
|
subscribeToCardEvents() {
|
|
3290
3331
|
this.outlets.forEach((outlet) => {
|
|
3291
3332
|
const instance = outlet.componentInstance;
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3333
|
+
if (instance && instance.onCardAction) {
|
|
3334
|
+
const subscription = instance.onCardAction.subscribe((action) => {
|
|
3335
|
+
this.onCardAction({ action: action.event, item: action.card });
|
|
3336
|
+
});
|
|
3337
|
+
this.cardEventSubs.push(subscription);
|
|
3338
|
+
}
|
|
3295
3339
|
});
|
|
3296
3340
|
}
|
|
3297
3341
|
clearcardEventSubs() {
|
|
@@ -3385,13 +3429,12 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3385
3429
|
this.doAction(actionEvent); // handle by father.
|
|
3386
3430
|
}
|
|
3387
3431
|
}
|
|
3388
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3389
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
3432
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3433
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: false, isRequired: false, transformFunction: null }, customCardComponent: { classPropertyName: "customCardComponent", publicName: "customCardComponent", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null }, gridLayout: { classPropertyName: "gridLayout", publicName: "gridLayout", isSignal: true, isRequired: false, transformFunction: null }, getCustomButtons: { classPropertyName: "getCustomButtons", publicName: "getCustomButtons", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions()\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout() }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions() }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onFilterAction", "onChangeSort"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
3390
3434
|
}
|
|
3391
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3435
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, decorators: [{
|
|
3392
3436
|
type: Component,
|
|
3393
3437
|
args: [{ selector: 'dc-agent-card-lists', imports: [
|
|
3394
|
-
CommonModule,
|
|
3395
3438
|
NgComponentOutlet,
|
|
3396
3439
|
PopoverModule,
|
|
3397
3440
|
ButtonModule,
|
|
@@ -3400,22 +3443,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3400
3443
|
SkeletonModule,
|
|
3401
3444
|
SpeedDialModule,
|
|
3402
3445
|
QuickTableComponent,
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
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: [{
|
|
3446
|
+
CommonModule,
|
|
3447
|
+
], 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"] }]
|
|
3448
|
+
}], ctorParameters: () => [], propDecorators: { viewMode: [{
|
|
3419
3449
|
type: Input
|
|
3420
3450
|
}], outlets: [{
|
|
3421
3451
|
type: ViewChildren,
|
|
@@ -3436,10 +3466,10 @@ class ParseCardPipe {
|
|
|
3436
3466
|
const result = this.builderConversation.applyReplacements(text, parseDict);
|
|
3437
3467
|
return result;
|
|
3438
3468
|
}
|
|
3439
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3440
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
3469
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
3470
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, isStandalone: true, name: "parseCard" }); }
|
|
3441
3471
|
}
|
|
3442
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3472
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, decorators: [{
|
|
3443
3473
|
type: Pipe,
|
|
3444
3474
|
args: [{
|
|
3445
3475
|
name: 'parseCard',
|
|
@@ -3448,13 +3478,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3448
3478
|
}] });
|
|
3449
3479
|
|
|
3450
3480
|
class DcAgentCardDetailsComponent {
|
|
3451
|
-
constructor(
|
|
3452
|
-
this.agentCardService =
|
|
3453
|
-
this.route =
|
|
3454
|
-
this.cdr =
|
|
3455
|
-
this.userDataExchange =
|
|
3481
|
+
constructor() {
|
|
3482
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
3483
|
+
this.route = inject(ActivatedRoute);
|
|
3484
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
3485
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
3456
3486
|
this.agentCardId = '';
|
|
3457
|
-
this.onStartConversation =
|
|
3487
|
+
this.onStartConversation = output();
|
|
3458
3488
|
this.showInfoLayer = false;
|
|
3459
3489
|
}
|
|
3460
3490
|
async ngOnInit() {
|
|
@@ -3480,22 +3510,14 @@ class DcAgentCardDetailsComponent {
|
|
|
3480
3510
|
this.showInfoLayer = !this.showInfoLayer;
|
|
3481
3511
|
this.cdr.markForCheck();
|
|
3482
3512
|
}
|
|
3483
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3484
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
3513
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3514
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { agentCardId: "agentCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\n <!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
|
|
3485
3515
|
}
|
|
3486
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3516
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
|
|
3487
3517
|
type: Component,
|
|
3488
|
-
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [
|
|
3489
|
-
}], ctorParameters: () => [
|
|
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: [{
|
|
3518
|
+
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"] }]
|
|
3519
|
+
}], ctorParameters: () => [], propDecorators: { agentCardId: [{
|
|
3496
3520
|
type: Input
|
|
3497
|
-
}], onStartConversation: [{
|
|
3498
|
-
type: Output
|
|
3499
3521
|
}] } });
|
|
3500
3522
|
|
|
3501
3523
|
/*
|
|
@@ -3507,5 +3529,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3507
3529
|
* Generated bundle index. Do not edit.
|
|
3508
3530
|
*/
|
|
3509
3531
|
|
|
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 };
|
|
3532
|
+
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, ProviderSelectorComponent, StandaloneAudioTextSyncComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, buildObjectTTSRequest, characterCardStringDataDefinition, defaultconvUserSettings, extractAudioAndTranscription, extractJsonFromResponse, matchTranscription, provideChatAIService, provideUserDataExchange };
|
|
3511
3533
|
//# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map
|